---
name: kosoor-merchant-api
description: Build, test or debug an integration with the Kosoor Merchant API (api.kosoor.com.sa), which reads and writes a Saudi business's customers, sales invoices, payments and credit notes, with ZATCA e-invoicing. Use when code calls api.kosoor.com.sa or holds a ksr_live_ or ksr_test_ key.
---

# Kosoor Merchant API

Kosoor is a point-of-sale and business system for businesses in Saudi Arabia. The Merchant API lets a partner's system work with one Kosoor merchant's data. The merchant issues the API key in the Kosoor dashboard (Settings → Developers); the partner needs no Kosoor account.

- Base URL: `https://api.kosoor.com.sa`. Every path starts with `/v1`. JSON, camelCase, HTTPS only.
- Docs index for agents: https://docs.kosoor.com.sa/llms.txt
- Every guide and every endpoint contract in one file: https://docs.kosoor.com.sa/llms-full.txt
- Any docs page as Markdown: add `.md` to its URL, for example https://docs.kosoor.com.sa/record-a-sale.md

Read `llms.txt` before writing code, then the pages the task needs. Do not guess a field, a value or an error code: they are all in the docs.

## Rules

1. Authenticate with `Authorization: Bearer <key>`. Keep the key on the server and read it from configuration.
2. Build and test with a test key (`ksr_test_` + 64 hex). It reaches only the merchant's sandbox. Check that replies carry `Kosoor-Mode: test`. A live key (`ksr_live_`) reaches real data: use it only when the user says the merchant is going live.
3. Send `Idempotency-Key` on every `POST` and `PATCH`: one key per logical operation (a UUID), the same key on every retry of it. Retry only timeouts, dropped connections, `409 idempotency_in_progress` and `5xx`.
4. Branch on `error.code`, never on `error.message`. Every code: https://docs.kosoor.com.sa/errors.md
5. Take branch, item and payment method ids from `GET /v1/branches`, `GET /v1/items` and `GET /v1/payment-methods`. Never hard-code or invent them.
6. Send only documented fields and query parameters. Anything else answers `400 unknown_parameter`.
7. Read money from the reply (`total`, `lines[].unitDiscount`, `amountPaid`, `amountDue`), not from the request. To pay an invoice in full, send `payment` without `amount`.
8. Every invoice names a customer. A company gets a standard invoice that ZATCA clears before the reply; a person gets a simplified invoice reported after it and must be paid at issue.
9. `zatca.status` is `pending`, `cleared`, `reported` or `rejected`. `pending` means issued, not failed. Never issue a sale again because it is `pending`.
10. Give every invoice, payment and credit note your own `referenceId`. On `409 reference_id_taken`, read `existingId` instead of creating another record.
11. Encode a phone in a query string: `phone=%2B966501234567`.
12. Log the `Request-Id` header of every call.

## Workflows

- Connect a merchant and make the first call: https://docs.kosoor.com.sa/onboarding.md
- Record a sale: https://docs.kosoor.com.sa/record-a-sale.md
- Refund or correct a sale: https://docs.kosoor.com.sa/refunds.md
- Keep an ERP in sync: https://docs.kosoor.com.sa/erp-sync.md
- ZATCA results: https://docs.kosoor.com.sa/zatca.md
- Go-live checklist: https://docs.kosoor.com.sa/go-live.md

## Testing

The sandbox holds fixture customers with fixed ids and known outcomes, such as a ZATCA rejection or an incomplete address: https://docs.kosoor.com.sa/test-mode.md

## Support

Kosoor support is on WhatsApp only: +966 12 606 1770 (https://wa.me/966126061770). No calls, no email. When something fails that the docs do not explain, tell the user to send support the `Request-Id`.
