API reference
Bill push (legacy)
The first API route, kept unchanged for systems already using it. New integrations use Bills.
Push a vendor bill (legacy)
POST/bills
Drafts a vendor bill with no VAT, matching the vendor by TRN and then by name, or creating it. Sending it again with the same vendor reference — or, for a bill with no reference, the same Idempotency-Key header — returns the first bill with duplicate: true. A bill with VAT is made from a purchase order: POST /api/v1/purchase-orders/{id}/bills.
Parameters
| Field | Type | Description |
|---|---|---|
Idempotency-KeyHeader | string |
Request body
16 fields
| Field | Type | Description |
|---|---|---|
vendor | object | |
vendor.name | string | |
vendor.trn | string | |
vendor.email | string | |
vendor.phone | string | |
reference | string | |
billDate | date | |
dueDate | date | |
lines | object[] | |
lines[].description | string | |
lines[].quantity | number | |
lines[].unitPrice | number | |
lines[].discount | number | |
lines[].uom | string | |
lines[].productCode | string | |
notes | string |
Code samples
curl -sS -X POST "https://app.kwn.tax/api/v1/bills" \
-H "X-Api-Key: $KWN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"vendor": {
"name": "Gulf Office Supplies L.L.C.",
"trn": "100456789100003"
},
"reference": "GOS-INV-88213",
"billDate": "2026-10-12",
"dueDate": "2026-11-11",
"lines": [
{
"description": "A4 copier paper, box of 5 reams",
"quantity": 20,
"unitPrice": 95
}
]
}'Responses
200OK
Field Type Description idstringnumberstringstatusDRAFT | SENT | CONFIRMED | RECEIVED | POSTED | PAID | CANCELLEDsubtotalnumbertaxTotalnumbertotalnumberduplicateboolean201Created
Field Type Description idstringnumberstringstatusDRAFT | SENT | CONFIRMED | RECEIVED | POSTED | PAID | CANCELLEDsubtotalnumbertaxTotalnumbertotalnumberduplicateboolean400Refused: the request or a business rule. The code says which (REQUEST_INVALID, RULE_REFUSED, …).
{ "error": "Lines: at least one line is required.", "code": "REQUEST_INVALID" }401No key, a key that is not ours, or one revoked (API_KEY_INVALID); a merchant not active (MERCHANT_INACTIVE).
{ "error": "That API key is not valid, or it was revoked.", "code": "API_KEY_INVALID" }403The key's scopes do not carry the capability (APP_NOT_GRANTED, ROLE_TOO_LOW), or the plan lacks the app (APP_LOCKED).
{ "error": "This key's scopes do not include sales.approve.", "code": "ROLE_TOO_LOW" }404Not found: another type under this route, or another company's (NOT_FOUND).
{ "error": "Not found.", "code": "NOT_FOUND" }409A conflict: an externalId taken, or a change that could not be saved (EXTERNAL_ID_TAKEN, CONFLICT).
{ "error": "That externalId is already used by another record of this company.", "code": "EXTERNAL_ID_TAKEN" }429Too many requests: wait retryAfter seconds (RATE_LIMITED).
{ "error": "Too many requests. Wait a little and try again.", "code": "RATE_LIMITED" }