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.
POST /brandswith the business website URL.POST /brands/{brandId}/extractto learn the brand.GET /brands/{brandId}/extractuntil status is complete.POST /brands/{brandId}/generateto start a batch. This spends credits.GET /brands/{brandId}/generationsfor 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
| Method | Path | What it does |
|---|---|---|
| GET | /brands | List brands |
| POST | /brands | Create 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}/extract | Poll extraction status |
| POST | /brands/{brandId}/extract | Start brand extraction |
| POST | /brands/{brandId}/generate | Start a generation batch |
| GET | /brands/{brandId}/generations | List completed generations |
| GET | /generations/{generationId} | Read one generation |
| GET | /billing/credits | Read 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.
read— GET endpoints: 60 requests/minute, burst 20write— Create, update, delete, and extraction triggers: 20 requests/minute, burst 5generate— Generation triggers: 5 requests/hour
Errors
| Status | Meaning | What to do |
|---|---|---|
| 401 | API key missing, malformed, invalid, or revoked. | Issue a new key from the dashboard. Do not retry. |
| 402 | Insufficient credits for the requested generation. | Top up credits, then retry. |
| 404 | Brand or generation does not exist, or is not owned by this key's user. | Do not retry with the same identifier. |
| 409 | Extraction is already in progress for this brand. | Poll the extraction status endpoint instead of starting another. |
| 422 | Validation failed, or the resource is in the wrong state. | Fix the request. Retrying unchanged will fail the same way. |
| 429 | Rate 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:
- /openapi.json — OpenAPI 3.1 description of this API
- /.well-known/ai-catalog.json — Agentic Resource Discovery catalog
- /.well-known/agent-skills/index.json — what 0layers can do, as skills
- /.well-known/agent-card.json — A2A agent card
- /.well-known/api-catalog — RFC 9727 API catalog
- /llms.txt — what 0layers is and when to reach for it
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.