1. Verify your key
Every request carries Authorization: Bearer. Your first call tells you which business and which
company the key is bound to, and what it may do:
curl -s "https://api.kumpara.net/v1/me" \
-H "Authorization: Bearer kp_live_a1b2c3d4_…"
{
"data": {
"tenant": { "id": "…", "name": "Örnek Ticaret A.Ş.", "plan": "Pro" },
"company": { "id": "…", "legalName": "Örnek Ticaret A.Ş.", "baseCurrency": "TRY" },
"apiClient": { "prefix": "kp_live_a1b2c3d4", "environment": "live",
"scopes": ["me:read", "contacts:write", "sales:write", "sales:approve"] }
},
"meta": { "requestId": "0HN…" }
}
GET /v1/capabilities then tells you whether e-documents are connected for this company, how many
credits are left and what the base currency is — you shape your flow around that.
2. Match the contact by your own key
You do not have to search for a customer every time: upsert by tax number or by contact code. An
existing record is updated, a missing one is created.
curl -s -X POST "https://api.kumpara.net/v1/contacts/actions/upsert?matchBy=taxNumber" \
-H "Authorization: Bearer kp_live_…" \
-H "Idempotency-Key: contact-4711-2026-09-23" \
-H "Content-Type: application/json" \
-d '{
"name": "Örnek Gıda Ltd. Şti.",
"type": "customer",
"taxNumber": "1234567890",
"preferredCurrency": "TRY",
"paymentTermDays": 30
}'
The response is { "data": { "id": "…", "name": "…", "created": true } }. The created flag tells you
whether a record was opened or an existing one updated. matchBy accepts only taxNumber or code.
3. Issue and approve the invoice
An invoice line has no amount field. You send quantity, unit price, discount and VAT rate; the line
total, the withheld VAT, the base amount and the document number are produced on the server.
curl -s -X POST "https://api.kumpara.net/v1/sales-invoices" \
-H "Authorization: Bearer kp_live_…" \
-H "Idempotency-Key: order-88213-invoice" \
-H "Content-Type: application/json" \
-d '{
"contactId": "6f1c…",
"currency": "TRY",
"approve": true,
"lines": [
{ "productId": "9a3e…", "quantity": 10, "unitPrice": 250, "vatRate": 20 },
{ "description": "Shipping", "quantity": 1, "unitPrice": 150, "vatRate": 20 }
]
}'
{
"data": { "id": "c41b…", "number": "SF-2026-000412", "status": "approved", "approved": true },
"meta": { "requestId": "0HN…" }
}
approve: true does creation and approval in one transaction: the document number, the customer's
debt and the stock issue appear together. That is a money mutation, so the key needs sales:approve
alongside sales:write — otherwise you get 403 insufficient_scope.
4. Record the payment and close the invoice
curl -s -X POST "https://api.kumpara.net/v1/payments" \
-H "Authorization: Bearer kp_live_…" \
-H "Idempotency-Key: payment-88213" \
-H "Content-Type: application/json" \
-d '{
"contactId": "6f1c…",
"cashAccountId": "2b77…",
"direction": "incoming",
"amount": 3060,
"invoiceType": "sales_invoice",
"invoiceId": "c41b…"
}'
The cash ledger, the customer ledger and the allocation are written in the same transaction. Use
allocations[] to split across several invoices, or POST /v1/payments/{id}/actions/allocate to settle later.
5. Pull the changes
To keep the other system current you poll the feed. It gives you the type and id of what changed;
you read the detail from its own endpoint.
curl -s "https://api.kumpara.net/v1/changes?since=2026-09-23T06:00:00Z&types=sales_invoice,payment" \
-H "Authorization: Bearer kp_live_…"
{
"data": [
{ "type": "sales_invoice", "id": "c41b…", "action": "created", "occurredAt": "2026-09-23T07:12:44Z" },
{ "type": "payment", "id": "77af…", "action": "created", "occurredAt": "2026-09-23T07:13:02Z" }
],
"meta": { "checkpoint": "2026-09-23T07:13:02Z", "nextCursor": null, "hasMore": false }
}
On the next call you pass meta.checkpoint as since — the bound is exclusive, so the same record
does not come twice. We recommend a five-minute overlap against the gap between commit order and
timestamp order; reads are idempotent, so a repeated record is harmless.