# Integration API

Catalog, order, and content API for an external system. A key only receives the abilities chosen in the admin: catalog:read, catalog:write, orders:read, orders:write-status, content:read, content:write. Prices are integer minor units: 49000 means 490.00. The same abilities will gate MCP tools.

Base URL: https://ouroborosbooks.com.ua/api/v1/integrations

Send the key on every request:

```
Authorization: Bearer {key}
Accept: application/json
```

## GET /products

List products Requires catalog:read. Paginated catalog. limit defaults to 15 and cannot exceed 50.

| Parameter | Where | Meaning |
| --- | --- | --- |
| `page` | query | Page number, starting at 1. |
| `limit` | query | Page size. Default 15, maximum 50. |
| `filter[name]` | query | Part of the product name. |
| `filter[sku]` | query | Exact SKU. |
| `include` | query | Pass offers to add variants. |

## GET /products/{id}

Show one product Requires catalog:read. Returns the product in data. include=offers adds variants. An unknown id returns 404.

| Parameter | Where | Meaning |
| --- | --- | --- |
| `id` | path | Product id in this store. |
| `include` | query | Pass offers to add variants. |

## PUT /catalog/products/{external_id}

Create or replace a product Requires catalog:write. Idempotent upsert by the caller external id. Body fields: name, sku, price, quantity, description, status, image_url, category_external_ids, tag_external_ids. image_url must be a public https image. Category and tag lists must already exist for this key and replace the product links.

| Parameter | Where | Meaning |
| --- | --- | --- |
| `external_id` | path | Caller identifier, stable across retries. |

## PATCH /catalog/products/{external_id}

Update part of a product Requires catalog:write. Changes only the supplied fields, including category_external_ids and tag_external_ids when present. The product must already exist for this key.

| Parameter | Where | Meaning |
| --- | --- | --- |
| `external_id` | path | Caller identifier, stable across retries. |

## PUT /catalog/categories/{external_id}

Create or rename a category Requires catalog:write. Body field: name.

| Parameter | Where | Meaning |
| --- | --- | --- |
| `external_id` | path | Caller identifier, stable across retries. |

## PUT /catalog/tags/{external_id}

Create or rename a tag Requires catalog:write. Body field: value. Status labels are rejected.

| Parameter | Where | Meaning |
| --- | --- | --- |
| `external_id` | path | Caller identifier, stable across retries. |

## GET /orders

List placed orders Requires orders:read. Cursor pagination with updated_since and cursor. Totals are minor units.

| Parameter | Where | Meaning |
| --- | --- | --- |
| `updated_since` | query | Only orders updated at or after this timestamp. |
| `cursor` | query | Return orders with a greater id. |
| `limit` | query | Page size. Default 15, maximum 50. |

## POST /orders/{reference}/status

Change an order status Requires orders:write-status and the Idempotency-Key header. Body field: status. Payment confirmation cannot be set here, and the paid total is not changed. A repeated key returns the first response. Keys with orders:read and a webhook URL receive a signed order.status_changed event.

| Parameter | Where | Meaning |
| --- | --- | --- |
| `reference` | path | Stable order reference. |

## GET /content/pages

List pages and posts Requires content:read. Cursor pagination. Optional type is page or blog.

| Parameter | Where | Meaning |
| --- | --- | --- |
| `type` | query | page or blog. |
| `cursor` | query | Return records with a greater id. |
| `limit` | query | Page size. Default 15, maximum 50. |

## PUT /content/pages/{external_id}

Create or update a draft page Requires content:write. Body fields: type (page or blog), title, slug, body. HTML is sanitized. Saving does not publish.

| Parameter | Where | Meaning |
| --- | --- | --- |
| `external_id` | path | Caller identifier, stable across retries. |

## POST /content/pages/{external_id}/publish

Publish a page Requires content:write and the Idempotency-Key header.

| Parameter | Where | Meaning |
| --- | --- | --- |
| `external_id` | path | Caller identifier, stable across retries. |

## POST /content/pages/{external_id}/unpublish

Unpublish a page Requires content:write.

| Parameter | Where | Meaning |
| --- | --- | --- |
| `external_id` | path | Caller identifier, stable across retries. |

## Fields

Product prices and offer prices are integer minor units.

- `id`: Product id in this store.
- `name`: string
- `description`: Plain text.
- `sku`: SKU of the first variant.
- `thumbnail_url`: string
- `quantity`: Sum of variant stock.
- `currency_code`: string
- `min_price`: Lowest variant price in minor units.
- `max_price`: Highest variant price in minor units.
- `weight`: number
- `length`: number
- `height`: number
- `width`: number
- `has_offers`: True when the product has more than one variant.
- `is_archived`: True when the product is not published.
- `category_id`: integer
- `created_at`: string
- `updated_at`: string
- `offers`: Present only when include=offers.

## Errors

- `401`: missing, unknown, or revoked key.
- `403`: the key has no `catalog:read` ability.
- `404`: the product id does not exist.
