Developer documentation

The 0layers public REST API turns a business website into on-brand social content. Version 1.0.0. The machine-readable description of everything below is at /openapi.json.

Base URL and authentication

Every request needs an API key, sent as a bearer token. Create one at /settings/api-keys while signed in. Keys start with 0l_live_ and are shown once at creation — store it then, because it cannot be read back.

Base URL: https://flexible-sandpiper-126.convex.site/api/v1
Authorization: Bearer 0l_live_…

Keys are created, listed, and revoked on that one screen, reachable from the account menu. There is no OAuth flow and no client registration endpoint; an agent acting for a user needs that user to paste in a key. See the authentication guide for the full walkthrough.

The standard sequence

Extraction and generation are asynchronous. Both return 202 and are polled through a matching read endpoint.

  1. POST /brands with the business website URL.
  2. POST /brands/{brandId}/extract to learn the brand.
  3. GET /brands/{brandId}/extract until status is complete.
  4. POST /brands/{brandId}/generate to start a batch. This spends credits.
  5. GET /brands/{brandId}/generations for finished content.

This API path is website-only. An Instagram profile URL returns INSTAGRAM_ONBOARDING_REQUIRED with status 422. Once its rollout gate is enabled, use Instagram setup in the signed-in dashboard, where the user provides the required profile authorization and the provider-specific cleanup lifecycle runs.

curl -sS "https://flexible-sandpiper-126.convex.site/api/v1/brands" \
  -X POST \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sourceUrl":"https://example.com"}'

Endpoints

MethodPathWhat it does
GET/brandsList brands
POST/brandsCreate a brand from a website
GET/brands/{brandId}Read one brand
PATCH/brands/{brandId}Update brand settings
DELETE/brands/{brandId}Delete a brand
GET/brands/{brandId}/extractPoll extraction status
POST/brands/{brandId}/extractStart brand extraction
POST/brands/{brandId}/generateStart a generation batch
GET/brands/{brandId}/generationsList completed generations
GET/generations/{generationId}Read one generation
GET/billing/creditsRead the credit balance

Response shapes

Successful reads return a data envelope. Lists add a pagination block with an opaque cursor. Errors return a stable machine-readable code alongside a human message; branch on the code, not the message.

{ "data": { … } }

{ "data": [ … ], "pagination": { "hasMore": true, "nextCursor": "…" } }

{ "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "…" } }

Rate limits

Limits are per API key, not per user. Exceeding one returns 429 with code RATE_LIMIT_EXCEEDED and a Retry-After header in seconds.

Every response carries a RateLimit-Policy header stating these limits, so a client can pace itself before it ever sees a 429. There is no live RateLimit-Remaining: the limiter reports whether a request was allowed, not how much budget is left, and a remaining count we cannot measure would be a number you would throttle against wrongly.

  • readGET endpoints: 60 requests/minute, burst 20
  • writeCreate, update, delete, and extraction triggers: 20 requests/minute, burst 5
  • generateGeneration triggers: 5 requests/hour

Errors

StatusMeaningWhat to do
401API key missing, malformed, invalid, or revoked.Issue a new key from the dashboard. Do not retry.
402Insufficient credits for the requested generation.Top up credits, then retry.
404Brand or generation does not exist, or is not owned by this key's user.Do not retry with the same identifier.
409Extraction is already in progress for this brand.Poll the extraction status endpoint instead of starting another.
422Validation failed, or the resource is in the wrong state.Fix the request. Retrying unchanged will fail the same way.
429Rate limit exceeded for this key.Back off until the bucket refills or the window rolls over.

Versioning

The version is in the path (/api/v1). Additive changes — new endpoints, new response fields, new error codes — ship within v1. Anything that would break an existing integration ships as a new path version instead. Treat unknown response fields as forward compatibility, not as an error.

Deprecation. An endpoint that is going away is announced on the responses you are already making: a Deprecation header (RFC 9745) marks it, and a Sunset header (RFC 8594) carries the date it stops working. Both appear at least 90 days before removal, so an integration finds out by running rather than by reading a changelog.

Discovery documents

Everything an agent needs to find and use 0layers without a human in the loop:

Every page on this site also serves Markdown: send Accept: text/markdown or append .md to the path.

Support

Integration questions, rate-limit increases, and bug reports go to hello@0layers.com. Include the endpoint, the error code, and roughly when the request was made.