REST API reference
Zubby exposes a public REST API at https://api.zubbyai.com/api/public/v1/. Use it to push your catalog and orders (the primary path for custom / headless stores) and to read conversations and leads back out into your CDP or warehouse. The API is JSON in, JSON out, cursor-paginated, and idempotent on writes.
Authentication
Every request carries a single header: a Bearer API key. The key is bound to one store, so there is no separate store-ID header — the store is derived from the key. Mint keys from Dashboard → Settings → API Keys. The secret is returned once at creation — store it in a secret manager, never commit it.
curl https://api.zubbyai.com/api/public/v1/conversations \
-H "Authorization: Bearer sp_live_<prefix>.<secret>"Keys carry scopes: read (the GET endpoints), write (catalog + order ingest), and admin (implies both). A request to a write endpoint with a read-only key returns 403.
Don’t expose keys client-side
Rate limits
60 requests / minute per API key (a sliding window, bucketed on the key). When you exceed it the gateway returns 429 with these headers:
Retry-After— seconds to wait before retrying.X-RateLimit-Limit— your ceiling (60).X-RateLimit-Remaining— calls left in the current window.X-RateLimit-Reset— unix epoch (ms) when the window resets.
For the high-volume initial catalog load, batch up to 200 products per POST /catalog rather than sending one request per product.
Pagination
The read endpoints (/conversations, /leads) use cursor pagination. Pass limit (max 100) and the next_cursor from the previous response as cursor:
# First page
curl "https://api.zubbyai.com/api/public/v1/conversations?limit=50" \
-H "Authorization: Bearer sp_live_<prefix>.<secret>"
# Response:
# {
# "data": [ ... ],
# "next_cursor": "2026-04-28T12:00:00.000Z"
# }
# Next page
curl "https://api.zubbyai.com/api/public/v1/conversations?limit=50&cursor=2026-04-28T12:00:00.000Z" \
-H "Authorization: Bearer sp_live_<prefix>.<secret>"The cursor is the ISO timestamp of the last row. When next_cursor is null, you’ve reached the end.
Errors
The endpoints return a non-2xx status with a JSON body. Validation failures include a details object from the schema:
{
"error": "Forbidden",
"message": "Missing scope: write"
}400—{ "error": "Bad request", "details": { ... } }(orInvalid JSON).401—{ "error": "Unauthorized" }: missing or invalid key.403—{ "error": "Forbidden", "message": "Missing scope: ..." }.422— every item in an ingest batch failed (seeerrors).429—{ "success": false, "error": { "code": "RATE_LIMIT_EXCEEDED", ... } }.
Partial success on ingest
200 with { "upserted", "received", "errors": [...] }. A non-empty errors array on a 200 means some items succeeded and some failed — always inspect it.Endpoint reference
Each endpoint group below lists the route, the HTTP method, a one- line summary, and an example body when relevant.
Catalog ingest
Push products + variants (write scope). Mainly for custom / headless stores; Shopify and Woo sync automatically. Idempotent on your externalId. Max 200 products per request.
/api/public/v1/catalogUpsert products + variants and queue embeddings. Re-send the same externalId to update in place.
{
"products": [
{
"externalId": "SKU-1",
"title": "Waxed Field Jacket",
"description": "Waxed cotton, charcoal.",
"status": "active",
"url": "https://shop.yourbrand.com/p/field-jacket",
"imageUrl": "https://cdn.yourbrand.com/jacket.jpg",
"tags": ["jackets"],
"variants": [
{ "externalId": "SKU-1-M", "title": "Medium", "price": 189.00, "inventory": 12 }
]
}
]
}Orders ingest
Push orders + line items (write scope) so order-status and reorder work on custom stores. Idempotent on externalId; line items are replaced on re-push.
/api/public/v1/ordersUpsert orders and their items. Re-push to update financial / fulfillment status.
{
"orders": [
{
"externalId": "1001",
"email": "shopper@example.com",
"total": 199.00,
"financialStatus": "paid",
"fulfillmentStatus": "unfulfilled",
"items": [
{ "title": "Waxed Field Jacket", "platformVariantId": "SKU-1-M", "quantity": 1, "unitPrice": 189.00 }
]
}
]
}Conversations
Read shopper ↔ AI sessions (read scope). Cursor-paginated. Mirror into your CDP or warehouse.
/api/public/v1/conversations?limit=50List conversations with status, intent, sentiment, summary, and tags.
Leads
Read captured leads (read scope). Cursor-paginated.
/api/public/v1/leads?limit=50List leads with email, name, purpose, status, and priority.
Idempotency
Ingest writes are idempotent on your own identifiers — no Idempotency-Key header needed. A product is keyed on its externalId, an order on its externalId. Re-sending the same payload updates the existing record in place instead of creating a duplicate, so safe retries and nightly full re-syncs Just Work.
SDKs
There’s no official SDK yet — the API is plain HTTP + JSON and works from any language with an HTTP client. If you’d like a typed client for a specific language, let us know and we’ll prioritize.
Adjacent
- Custom / headless integration — the end-to-end guide to connecting a store with this API.
- API keys — minting read vs. write keys and revoking them.
- Outbound webhooks — push, not pull.
- Security & privacy — how we handle your API keys.