> ## Documentation Index
> Fetch the complete documentation index at: https://www.adorabl.info/llms.txt
> Use this file to discover all available pages before exploring further.

# Partner API

> Authenticate with OAuth 2.0 client credentials and read or write customers, suppliers, items, and sales orders for one adorabl organization.

The Partner API is a REST interface for integrations that need to read or write master data and sales orders in an adorabl organization. It uses OAuth 2.0 client credentials: an administrator issues a client under [API clients](https://www.adorabl.info/admin-and-settings/api-clients.md), your integration exchanges that client for a short-lived access token, and every call is scoped to the issuing organization.

> [!NOTE]
>
> This section is written for engineers and integrators. Operators who need to issue or revoke credentials should start with [API clients](https://www.adorabl.info/admin-and-settings/api-clients.md).

## Base URL

All endpoints are served from the adorabl application origin, currently `https://sherpa-topaz.vercel.app`. Resource endpoints live under `/api/v1`; the token endpoint is `/api/oauth/token`.

## Authentication

**1. Obtain client credentials**

An organization admin issues a client under **Settings → Data & Admin → API clients** and grants it scopes. The client secret is shown once.

**2. Request an access token**

Send a `client_credentials` grant to `POST /api/oauth/token`. The body may be form-encoded or JSON.

```bash theme={null}
curl -X POST https://sherpa-topaz.vercel.app/api/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d grant_type=client_credentials \
  -d client_id="$SHERPA_CLIENT_ID" \
  -d client_secret="$SHERPA_CLIENT_SECRET"
```

```json theme={null}
{
  "access_token": "<jwt>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "customers:read items:read sales_orders:write"
}
```

Tokens expire after one hour. Request a new one rather than caching past `expires_in`.

**3. Call the API**

Send the token as a bearer header on every resource request.

```bash theme={null}
curl https://sherpa-topaz.vercel.app/api/v1/items?limit=50 \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

The token carries the organization and the scopes the client held when it was minted. Rotating or revoking the client stops new tokens; a token already issued remains valid until it expires.

## Scopes

| Scope | Allows |
| - | - |
| `customers:read` | `GET /api/v1/customers`, `GET /api/v1/customers/{id}` |
| `customers:write` | `POST /api/v1/customers`, `PUT /api/v1/customers/{id}` |
| `suppliers:read` | `GET /api/v1/suppliers`, `GET /api/v1/suppliers/{id}` |
| `suppliers:write` | `POST /api/v1/suppliers`, `PUT /api/v1/suppliers/{id}` |
| `items:read` | `GET /api/v1/items`, `GET /api/v1/items/{id}` |
| `items:write` | `POST /api/v1/items`, `PUT /api/v1/items/{id}` |
| `sales_orders:read` | `GET /api/v1/sales-orders`, `GET /api/v1/sales-orders/{id}` |
| `sales_orders:write` | `POST /api/v1/sales-orders`, `PUT /api/v1/sales-orders/{id}` |

A request without the required scope returns `403` with `{"error": "insufficient_scope", "details": {"required": "<scope>"}}`.

## Conventions

### Response envelope

Every successful response wraps the payload in `data`.

* **Get by id** returns a single object: `{ "data": { ... } }`.
* **List** returns an array plus a cursor: `{ "data": [ ... ], "nextCursor": "<opaque>" | null }`.
* **Create** returns `201` with the same shape as a get.
* **Update** returns `200` with the same shape as a get.

### Shape guarantees

* Child collections — `addresses`, `contacts`, `uomConversions`, `lines` — are always present. An empty collection is `[]`, never omitted.
* Optional singular references — `terms`, `customerGroup`, `supplierGroup`, `paymentMethod`, `category`, `preferredSupplier`, `baseUom`, `customer`, `warehouse`, `shipTo`, `billTo` — are `null` when unset, never omitted. Supplier `terms` is a string.
* Never send an empty object `{}` for a missing child; omit the field or send `null`.

### Pagination

List endpoints accept `limit` (1–200, default 50) and `cursor`. Pass the `nextCursor` from one page as the `cursor` of the next until `nextCursor` is `null`. Cursors are opaque; do not construct them.

### Writes

* Send the record inside `{ "data": { ... } }`.
* Server-owned fields — `id`, `customerNumber`, `supplierNumber`, `sku` on update, `orderNumber`, timestamps, totals, and child `id`s — are ignored and recomputed.
* `PUT` is a partial header update: omitted header fields keep their current values. Omitted child collections are left unchanged; send `[]` to clear one.
* Create and update rules match the adorabl screens. For example, a sales order `POST` creates a **draft**, and `PUT /api/v1/sales-orders/{id}` only succeeds while the order is still a draft — later statuses return `409`.

### Errors

Errors return a JSON body with a stable `error` code and optional `details`.

| Status | Meaning |
| - | - |
| `400` | Invalid id, cursor, limit, JSON, or payload — `details` names the problem |
| `401` | Missing or invalid token, or invalid client credentials on the token endpoint |
| `403` | Token lacks the required scope |
| `404` | Record not found in the token's organization |
| `409` | State conflict, such as updating a non-draft sales order |

```json theme={null}
{ "error": "insufficient_scope", "details": { "required": "items:write" } }
```

## Example: create a draft sales order

```bash theme={null}
curl -X POST https://sherpa-topaz.vercel.app/api/v1/sales-orders \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": {
      "customer": { "customerNumber": "CUST-00042" },
      "orderDate": "2026-10-02",
      "channel": "b2b_portal",
      "externalId": "PORTAL-10422",
      "lines": [
        { "item": { "sku": "AP-3300-NV" }, "qtyOrdered": 12, "unitPrice": 84.00 }
      ]
    }
  }'
```

References accept either the adorabl `id` or a natural key — `customerNumber` for customers, `sku` for items. The response is the full sales order with computed totals and `lines` populated; drafts receive their `orderNumber` when they are submitted in adorabl. Submit, fulfill, and invoice the order from the adorabl screens — see [Sales orders & fulfillment](https://www.adorabl.info/commerce/sales-orders-and-fulfillment.md).

## Endpoint reference

The **Customers**, **Suppliers**, **Items**, and **Sales orders** sections in the sidebar are generated from the OpenAPI document and include request and response schemas plus an interactive playground for every endpoint. Paste a bearer token from `POST /api/oauth/token` into the playground's authorization field to try calls against your organization.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.