# Connect a merchant

> Every step from the merchant signing in to the Kosoor dashboard to your first call with a test key, then going live.

The merchant does the first steps in the Kosoor dashboard and you do the rest. If the merchant has never issued a key, send them this page.

## 1. The merchant signs in

The merchant signs in to the Kosoor dashboard with a user that has the permission `integration.api_keys.management`. Without it, **Developers** does not appear under **Settings**.

## 2. Open Settings → Developers

The **Developers** page has five tabs:

| Tab | What it holds |
|---|---|
| **Overview** | the merchant's keys, the sandbox, and the calls of the last 30 days |
| **API keys** | every key: issue, edit, revoke |
| **Request log** | every call a key made in the last 30 days |
| **Webhooks** | the endpoints keys registered, and every event sent to them; see [Webhooks](https://docs.kosoor.com.sa/webhooks.md) |
| **Sandbox** | the separate copy of the catalogue that test keys reach |

![The Developers page on its Overview tab](https://docs.kosoor.com.sa/img/onboarding/developers-overview.en.png.md)

## 3. Create the sandbox

On the **Sandbox** tab, the merchant clicks **Create sandbox**. Kosoor copies the merchant's branches, items, payment methods and chart of accounts into it; customers and invoices are not copied. Preparing it takes a few minutes, and test keys answer `503 sandbox_unavailable` until the tab shows **Sandbox ready**.

The sandbox keeps the live ids of what it copies, so a branch, item or payment method id you use in the sandbox is the same id on live data. Customers are not copied, so their ids differ.

![The Sandbox tab showing Sandbox ready](https://docs.kosoor.com.sa/img/onboarding/sandbox-ready.en.png.md)

## 4. Issue a test key

On the **API keys** tab, the merchant clicks **Issue a key** and fills in the form:

| Field | What to choose |
|---|---|
| **Key mode** | **Test**. A test key reaches the sandbox only. It can be chosen once the sandbox exists. |
| **Key name** | Your system's name. The name is how the merchant tells which system uses the key on the day it has to be revoked. |
| **Permissions** | For each resource: **None**, **Read**, or **Read and write**. Grant the least that is enough; the table below says what each task needs. |
| **Branches** | The branches the key may use. Leave all unticked and the key works in every branch. |
| **Expires on** | Optional. The key stops at the start of this day. Kosoor sends a reminder email 14 days and then 3 days before. |

![The Issue a key form with Test selected](https://docs.kosoor.com.sa/img/onboarding/issue-test-key.en.png.md)

The key is shown once, with the note **Copy the key now: it is not shown again**. The merchant copies it and sends it to you. Once that message is closed the key cannot be shown again: if it is lost, the merchant revokes it and issues a new one. A merchant can hold at most 50 active keys of each mode.

![The new key, shown once, with its Copy button](https://docs.kosoor.com.sa/img/onboarding/key-shown-once.en.png.md)

### Permissions each task needs

| To | Grant |
|---|---|
| read the catalogue: branches, items, payment methods | `branches:read`, `items:read`, `payment_methods:read` |
| find and create customers | `customers:write` |
| issue sales invoices | `sales_invoices:write` |
| record payments after the sale | `payments:write` |
| issue credit notes | `credit_notes:write` |
| only read, for example to mirror data into an ERP | the `read` level of each resource you read |

`write` includes `read`. A call without the permission it needs answers `403 missing_permission`, and `requiredPermission` in the error names it. See [Authentication](https://docs.kosoor.com.sa/authentication.md).

## 5. Make your first call

Read one of the customers every sandbox holds, with the test key:

```bash
curl -i https://api.kosoor.com.sa/v1/customers/5a4db0c5-0000-4000-8000-000000000001 \
  -H "Authorization: Bearer ksr_test_..."
```

```http
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Kosoor-Mode: test
Request-Id: req_01JC3W8Z9Q4M2T7XKD5R6VBN0P
```

```json
{
  "id": "5a4db0c5-0000-4000-8000-000000000001",
  "object": "customer",
  "type": "person",
  "referenceId": null,
  "firstName": "Sandbox Person",
  "lastName": null,
  "phone": "+966500000001",
  "email": null,
  "isActive": true,
  "branchId": null,
  "company": null,
  "address": {
    "street": null,
    "buildingNumber": null,
    "postalCode": null,
    "district": null,
    "city": null,
    "country": null
  },
  "metadata": {},
  "standardInvoiceMissing": null,
  "createdAt": "2026-11-07T09:12:40.513Z"
}
```

`Kosoor-Mode: test` says the call reached the sandbox. A live key's replies do not carry it.

To make the same calls from Postman, import the [Postman collection](https://docs.kosoor.com.sa/postman/kosoor-merchant-api.postman_collection.json) and the [Kosoor test](https://docs.kosoor.com.sa/postman/kosoor-test.postman_environment.json) environment, and set `apiKey` to the test key. The collection holds every endpoint and is generated from the [OpenAPI document](https://docs.kosoor.com.sa/openapi.json). When you go live, switch to the [Kosoor live](https://docs.kosoor.com.sa/postman/kosoor-live.postman_environment.json) environment with the live key.

## 6. Find the call in the Request log

On the **Request log** tab, the merchant sees every call a key made in the last 30 days, newest first. It filters by key mode (**Live** or **Test**), key, status, path and date. **Details** shows the call's `Request-Id`. Request and response bodies are kept only for refused and failed calls.

![The Request log tab with the first call and its details open](https://docs.kosoor.com.sa/img/onboarding/request-log.en.png.md)

## 7. Build in the sandbox

Build the whole flow with the test key: [Record a sale](https://docs.kosoor.com.sa/record-a-sale.md), [Refund or correct a sale](https://docs.kosoor.com.sa/refunds.md), and whatever else your system does. The sandbox holds fixture customers with known outcomes, so you can test ZATCA rejections and other refusals without arranging the data. See [Test mode](https://docs.kosoor.com.sa/test-mode.md).

## 8. Go live

When your system works in the sandbox:

1. The merchant issues a second key with **Key mode** set to **Live**, the same permissions and the same branches.
2. You replace the test key with the live key. Nothing else changes: same host, same paths, and the same ids for branches, items and payment methods.
3. You make one real call and the merchant checks it in the **Request log**, under **Live**.

Go through the [go-live checklist](https://docs.kosoor.com.sa/go-live.md) before the first real sale.

To stop a key, the merchant revokes it on the **API keys** tab. Every system using it stops at once, with `401 revoked_api_key`.

Source: https://docs.kosoor.com.sa/onboarding
