API reference
Requests for quotation
Requests for quotation to vendors, confirmed into purchase orders.
- POSTCreate a request for quotation
- GETList
- PUTUpdate an RFQ
- GETRead one
- POSTConfirm into a purchase order
- POSTCancel an RFQ
- GETPrint
Create a request for quotation
POST/rfqs
Send an Idempotency-Key header.
A draft RFQ. Lines name a product by productId or productCode. Requires Idempotency-Key.
Parameters
| Field | Type | Description |
|---|---|---|
Idempotency-KeyHeaderRequired | string | 1 to 100 visible ASCII characters, such as a UUID, unique to this request. Sent again with the same request, the first answer is replayed (Idempotent-Replayed: true); with another request, 422 IDEMPOTENCY_KEY_REUSED. |
Request body
20 fields
| Field | Type | Description |
|---|---|---|
lines | object[] | |
lines[].unitPrice | number | |
lines[].quantity | number | |
lines[].taxRate | number | VAT percent: 5 or 0. |
lines[].description | string | |
lines[].productId | string | |
lines[].productCode | string | A product's SKU, then its barcode. |
lines[].uom | string | |
lines[].discount | number | |
lines[].kind | PRODUCT | SECTION | NOTE | |
vendorId | string | The vendor contact; needed to confirm. |
issueDate | date | |
dueDate | date | |
vendorRef | string | The vendor's own reference. |
notes | string | |
orderDeadline | date | |
expectedArrival | date | |
buyerId | string | |
paymentTermId | string | |
externalId | string | Your system's id for it, kept for good. Create only. |
Code samples
curl -sS -X POST "https://app.kwn.tax/api/v1/rfqs" \
-H "X-Api-Key: $KWN_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"vendorId": "ven_7xp3k8d2",
"issueDate": "2026-10-15",
"vendorRef": "Q-2026-118",
"externalId": "PO-5521",
"lines": [
{
"description": "A4 copier paper, box of 5 reams",
"quantity": 20,
"unitPrice": 95,
"taxRate": 5,
"productCode": "PAP-A4-5"
}
]
}'Responses
201Created
Field Type Description idstringtypeRFQ | ORDER | BILL | REFUND | DEBIT_NOTEnumberstringstatusDRAFT | SENT | CONFIRMED | RECEIVED | POSTED | PAID | CANCELLEDissueDatedatedueDatedatevendorobjectvendorRefstringbuyerIdstringpaymentTermIdstringnotesstringorderDeadlinedateexpectedArrivaldatelinesobject[]subtotalnumberdiscountTotalnumbertaxTotalnumbertotalnumberpaidTotalnumbercreditedTotalnumberoriginMANUAL | API | EINVOICEsourceobjectderivedobject[]externalIdstringcreatedAtdateupdatedAtdate400Refused: 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" }422That Idempotency-Key was used for a different request (IDEMPOTENCY_KEY_REUSED).
{ "error": "That Idempotency-Key was used for a different request.", "code": "IDEMPOTENCY_KEY_REUSED" }429Too many requests: wait retryAfter seconds (RATE_LIMITED).
{ "error": "Too many requests. Wait a little and try again.", "code": "RATE_LIMITED" }
List
GET/rfqs
Newest first, a page at a time. Filter by status, partnerId (the vendor), issueFrom/issueTo, number, externalId, createdFrom/createdTo, or updatedSince.
Parameters
| Field | Type | Description |
|---|---|---|
statusQuery | string | A status, by name. |
partnerIdQuery | string | The customer (sales) or the vendor (purchases). |
issueFromQuery | date | Issued on or after this day. |
issueToQuery | date | Issued on or before this day. |
numberQuery | string | |
limitQuery | integer | 1 to 200; 50 by default. |
cursorQuery | string | The previous page's next. |
updatedSinceQuery | date | Only rows changed at or after this instant. |
createdFromQuery | date | |
createdToQuery | date | |
externalIdQuery | string | Only the row your system gave this id. |
Code samples
curl -sS -X GET "https://app.kwn.tax/api/v1/rfqs" \
-H "X-Api-Key: $KWN_API_KEY"Responses
200OK
Field Type Description dataobject[]The rows of this page. nextstringThe cursor of the next page, to send as ?cursor=; null on the last page. 400Refused: 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" }
Update an RFQ
PUT/rfqs/{id}
Replaces the header and the lines of an open RFQ.
Parameters
| Field | Type | Description |
|---|---|---|
idPathRequired | string |
Request body
20 fields
| Field | Type | Description |
|---|---|---|
lines | object[] | |
lines[].unitPrice | number | |
lines[].quantity | number | |
lines[].taxRate | number | VAT percent: 5 or 0. |
lines[].description | string | |
lines[].productId | string | |
lines[].productCode | string | A product's SKU, then its barcode. |
lines[].uom | string | |
lines[].discount | number | |
lines[].kind | PRODUCT | SECTION | NOTE | |
vendorId | string | The vendor contact; needed to confirm. |
issueDate | date | |
dueDate | date | |
vendorRef | string | The vendor's own reference. |
notes | string | |
orderDeadline | date | |
expectedArrival | date | |
buyerId | string | |
paymentTermId | string | |
externalId | string | Your system's id for it, kept for good. Create only. |
Code samples
curl -sS -X PUT "https://app.kwn.tax/api/v1/rfqs/<id>" \
-H "X-Api-Key: $KWN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"vendorId": "ven_7xp3k8d2",
"issueDate": "2026-10-15",
"vendorRef": "Q-2026-118",
"externalId": "PO-5521",
"lines": [
{
"description": "A4 copier paper, box of 5 reams",
"quantity": 20,
"unitPrice": 95,
"taxRate": 5,
"productCode": "PAP-A4-5"
}
]
}'Responses
200OK
Field Type Description idstringtypeRFQ | ORDER | BILL | REFUND | DEBIT_NOTEnumberstringstatusDRAFT | SENT | CONFIRMED | RECEIVED | POSTED | PAID | CANCELLEDissueDatedatedueDatedatevendorobjectvendorRefstringbuyerIdstringpaymentTermIdstringnotesstringorderDeadlinedateexpectedArrivaldatelinesobject[]subtotalnumberdiscountTotalnumbertaxTotalnumbertotalnumberpaidTotalnumbercreditedTotalnumberoriginMANUAL | API | EINVOICEsourceobjectderivedobject[]externalIdstringcreatedAtdateupdatedAtdate400Refused: 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" }
Read one
GET/rfqs/{id}
The document with its lines and what it came from and led to. A document of another type under this route is NOT_FOUND.
Parameters
| Field | Type | Description |
|---|---|---|
idPathRequired | string |
Code samples
curl -sS -X GET "https://app.kwn.tax/api/v1/rfqs/<id>" \
-H "X-Api-Key: $KWN_API_KEY"Responses
200OK
Field Type Description idstringtypeRFQ | ORDER | BILL | REFUND | DEBIT_NOTEnumberstringstatusDRAFT | SENT | CONFIRMED | RECEIVED | POSTED | PAID | CANCELLEDissueDatedatedueDatedatevendorobjectvendorRefstringbuyerIdstringpaymentTermIdstringnotesstringorderDeadlinedateexpectedArrivaldatelinesobject[]subtotalnumberdiscountTotalnumbertaxTotalnumbertotalnumberpaidTotalnumbercreditedTotalnumberoriginMANUAL | API | EINVOICEsourceobjectderivedobject[]externalIdstringcreatedAtdateupdatedAtdate400Refused: 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" }
Confirm into a purchase order
POST/rfqs/{id}/confirm
Confirms the RFQ into its purchase order and answers the order. Confirmed already: alreadyDone with that order.
Parameters
| Field | Type | Description |
|---|---|---|
idPathRequired | string |
Code samples
curl -sS -X POST "https://app.kwn.tax/api/v1/rfqs/<id>/confirm" \
-H "X-Api-Key: $KWN_API_KEY"Responses
200OK
Field Type Description alreadyDonebooleanTrue when the document was already where the action takes it; nothing changed. documentobject400Refused: 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" }
Cancel an RFQ
POST/rfqs/{id}/cancel
Cancelled already: alreadyDone.
Parameters
| Field | Type | Description |
|---|---|---|
idPathRequired | string |
Code samples
curl -sS -X POST "https://app.kwn.tax/api/v1/rfqs/<id>/cancel" \
-H "X-Api-Key: $KWN_API_KEY"Responses
200OK
Field Type Description alreadyDonebooleanTrue when the document was already where the action takes it; nothing changed. documentobject400Refused: 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" }
GET/rfqs/{id}/pdf
The printed document as the app prints it, in English or Arabic (?lang=ar). Counted against the apikey-heavy budget.
Parameters
| Field | Type | Description |
|---|---|---|
idPathRequired | string | |
langQuery | string |
Code samples
curl -sS -X GET "https://app.kwn.tax/api/v1/rfqs/<id>/pdf" \
-H "X-Api-Key: $KWN_API_KEY"Responses
200OK
400Refused: 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" }