Ascend API
Programmatically create customers, products, and invoices, and receive webhook events when invoices are sent or paid.
Authentication
All requests need a Bearer token. Create one in Settings → Developer API.
Authorization: Bearer ak_live_xxxxxxxxxxxxxxxx
Each key is scoped to one business. To work across businesses, mint a key per business.
Base URL
https://ascendinvoice.com/api/public/v1
Customers
POST /customers
curl https://ascendinvoice.com/api/public/v1/customers \
-H "Authorization: Bearer ak_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Inc",
"email": "billing@acme.com",
"country": "IN"
}'GET /customers · GET /customers/:id
curl https://ascendinvoice.com/api/public/v1/customers?limit=50 \ -H "Authorization: Bearer ak_live_xxx"
Products
POST /products
curl https://ascendinvoice.com/api/public/v1/products \
-H "Authorization: Bearer ak_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"name": "Consulting hour",
"unit_price": 5000,
"default_tax_rate": 18,
"currency": "INR",
"type": "service"
}'Invoices
POST /invoices
curl https://ascendinvoice.com/api/public/v1/invoices \
-H "Authorization: Bearer ak_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "uuid-from-customers",
"currency": "INR",
"due_date": "2026-07-15",
"status": "sent",
"items": [
{ "description": "Consulting", "quantity": 4, "unit_price": 5000, "tax_rate": 18 }
]
}'The invoice number is assigned automatically using your business's prefix and counter. Totals are computed from items.
GET /invoices · GET /invoices/:id
curl https://ascendinvoice.com/api/public/v1/invoices?status=sent&limit=20 \ -H "Authorization: Bearer ak_live_xxx"
Webhooks
Add an endpoint URL in Settings → Developer API. We'll POST JSON events with a signature header.
X-Ascend-Signature: t=1719500000,v1=<hex hmac>
X-Ascend-Event: invoice.paid
Content-Type: application/json
{
"id": "evt_…",
"type": "invoice.paid",
"created": 1719500000,
"data": { "invoice": { ... } }
}Verify the signature with your endpoint's signing secret:
const [tPart, sigPart] = header.split(",");
const ts = tPart.split("=")[1];
const sig = sigPart.split("=")[1];
const expected = crypto
.createHmac("sha256", SIGNING_SECRET)
.update(`${ts}.${rawBody}`)
.digest("hex");
// timing-safe compare(expected, sig)Available events: invoice.created, invoice.sent, invoice.paid. Use * to receive all.
Errors
{ "error": { "code": "validation_error", "message": "..." } }HTTP status codes: 200/201 success, 401 bad auth, 404 not found, 422 validation, 500 server error.