# Sokisoko API — guide for agents

Sokisoko is a multi-tenant white-label commerce platform: each tenant runs their own
storefront, optionally kept in step with the ERP they already run (SAP Business One,
Odoo and others). One API serves every tenant; the tenant is resolved per request.

- **Base URL:** `https://api.sokisoko.com`
- **Machine-readable description:** [OpenAPI 3.1](https://sokisoko.com/openapi.yaml)
- **Catalog:** [`/.well-known/api-catalog`](https://sokisoko.com/.well-known/api-catalog)
- **Authentication:** [`/auth.md`](https://sokisoko.com/auth.md)
- **Health:** `GET /healthz` (liveness), `GET /readyz` (readiness)

## Picking a tenant

Every storefront endpoint is scoped to one tenant's website, resolved from the request
host. Calling `api.sokisoko.com` directly means you must name the storefront yourself:

```
X-Tenant-Host: alpha.sokisoko.com
```

Use the store's own public hostname. Three outcomes:

| Host                                | Result                                     |
| ----------------------------------- | ------------------------------------------ |
| A tenant storefront host            | `200`, that tenant's data                  |
| `sokisoko.com` / `www.sokisoko.com` | `404 platform_host` — the apex is not a store |
| Anything else                       | `404 store_not_found`                      |

There is no cross-tenant endpoint and no tenant directory: an agent works against a
storefront it was pointed at.

## Authentication

Requests are either public or bearer-authenticated:

```
Authorization: Bearer <token>
```

Two token kinds, both in the same header:

- **API key** — `tgk_…`, minted by a tenant admin (Settings → API keys). Scopes can
  never exceed those of the admin who created it. Best fit for agents.
- **Session JWT** — issued by `POST /storefront/auth/login` (buyers) or
  `POST /admin/auth/login` (staff). Short-lived.

Tokens are audience-bound: an `admin` token is rejected on vendor routes and vice
versa. See [`/auth.md`](https://sokisoko.com/auth.md) for the full flow.

## Reading a catalog without credentials

These need no token — only the tenant host:

| Endpoint                                    | Returns                                       |
| ------------------------------------------- | --------------------------------------------- |
| `GET /storefront/config`                    | Capability flags (e.g. self-signup enabled)   |
| `GET /storefront/sitemap`                   | Indexable product / category / page URLs      |
| `GET /storefront/products`                  | Paged product list — `q`, `category`, `filter`, `page`, `page_size` |
| `GET /storefront/products/{slug}`           | One product — `locale` for translated fields  |
| `GET /storefront/products/{slug}/variants`  | Variant axes and selectable siblings          |
| `GET /storefront/products/{slug}/related`   | Cross-sell products in the same category      |
| `GET /storefront/locales`                   | Locales the catalog is translated into        |

`GET /storefront/currencies` (the currencies the store trades in) sits behind a
storefront token, like pricing and the cart.

Example:

```
curl -H 'X-Tenant-Host: alpha.sokisoko.com' \
  'https://api.sokisoko.com/storefront/products?q=cable&page_size=10'
```

## Prices are per-buyer, so they need a token

List prices are public; the price a given buyer actually pays is not. Contract tiers,
customer-specific pricing and live ERP stock resolve against the authenticated
account, and are read through `GET /storefront/products/{slug}/pricing`,
`GET /storefront/products/{slug}/availability` and the cart. An unauthenticated agent
should treat catalog prices as indicative.

## Errors

Every non-2xx response is the same envelope:

```json
{ "code": "store_not_found", "message": "no store is configured for this address" }
```

Match on `code`, not on `message`. `401 unauthorized` means the token is missing or
expired; `403 forbidden` means it is valid but lacks the scope.

## Getting a tenant

Self-serve signup is invite-only: the team sets up every tenant after a demo request
(form at <https://sokisoko.com/#demo>). The one public write on the platform is the
lead endpoint — no token, no tenant host:

```
POST https://api.sokisoko.com/platform/leads
Content-Type: application/json

{"contact_name":"Jane Mwangi","company_name":"Acme Ltd","email":"jane@acme.co.ke","phone":"+2547…","notes":"B2B storefront on SAP B1"}
```

`contact_name` is required, plus at least one of `email` or `phone`; `company_name`
and `notes` are optional. Success is `201 {"ok":true}`; failures use the standard
error envelope. Requests are rate-limited per client IP.

## Rate limits

Credential endpoints (login, register, password reset) are capped per client IP —
10 requests per minute by default. Over the cap you get
`429 {"code":"rate_limited"}` with a `Retry-After` header in seconds; wait it out
rather than retrying immediately. Catalog reads are not currently capped, but treat
`429` as possible on any endpoint and honour `Retry-After`.

## Contact

[hello@sokisoko.com](mailto:hello@sokisoko.com)
