Chaviar
Home Sign in Get started

Developers

API Reference

Last updated: September 16, 2026

Create, retrieve, review and schedule brand content with the Chaviar public API.

Contents

Authentication and permissionsWorkflow and creationImages and billingRequests and current statusEndpoint referenceKey rotation and limitsWebhook deliveryErrors and retry handling
View raw Markdown OpenAPI 3.1 JSON

Base URL: https://chaviar.app/api/v1

Posts, stories and reels use the app's generation and publishing pipelines. Email and ad creation produces drafts. Ad budgets, email sending and comment replies stay in the app.

Authentication and permissions

Send Authorization: Bearer cvk_... on every API call. Create a key in Settings → API keys; only the workspace owner can create or revoke keys. A key belongs to one brand; request bodies cannot choose a different tenant. Pro permits one active key family; Studio permits five. Free and Starter do not include API access.

Creation grants select post, story, reel, email and ad independently. Existing keys retain their existing grants. The publishing grant is off by default. A key with publishing permission still needs the content:publish scope to use auto mode or approve, schedule or publish content. LinkedIn requires human review in the app, including when the key has publishing permission.

ScopeAccess
content:readContent, requests, channels, calendar, wallet, usage, own key policy
content:writeValidate/create content, edit captions, cancel/restore drafts, regenerate
content:publishApprove, schedule, publish and create in auto mode; also needs content:write and publishing grant
library:readBrand media library
insights:readCached performance metrics
webhooks:manageWebhook subscriptions, delivery history and replay
keys:rotateRotate the current key or reduce its family's policy

Keys created with the existing Settings controls start with all scopes. Use PATCH /key to remove scopes, restrict allowed_platforms, lower daily_token_limit, or set allow_auto_publish to false. The API cannot add authority. These reductions apply to every credential in the rotation family. Reads with the appropriate scope cover the brand; kind and destination restrictions apply to creation and mutations. Use separate brands for separate tenants.

Workflow and creation

  1. Read GET /channels and GET /wallet.
  2. Optionally call POST /content/validate with the creation body.
  3. Call POST /content with a stable idempotency_key.
  4. Poll GET /requests/{id} every few seconds, or subscribe to webhooks.
  5. When generation finishes, retrieve each delivery's content_id with GET /content/{id}.
  6. Edit or schedule eligible drafts through the API, or open their review_url in the app. Select the owning brand in the app first.
{
  "kind": "post",
  "brief": "Announce our new Thursday evening opening hours.",
  "platforms": ["ig", "fb"],
  "mode": "review",
  "idempotency_key": "hours-announcement-v1",
  "metadata": {"campaign": "autumn-hours"}
}
curl https://chaviar.app/api/v1/content \
  -H "Authorization: Bearer $CHAVIAR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"kind":"post","brief":"Announce our new Thursday evening opening hours.","mode":"review","idempotency_key":"hours-announcement-v1"}'

A new accepted request returns HTTP 202, an id, status composing, kind, mode and created_at. The Location header points to /api/v1/requests/{id}. Acceptance means queued, not generated or published.

Creation fieldContract
kindRequired: post, story, reel, email or ad
briefRequired non-empty text, at most 2,000 characters
modereview by default; auto requires publishing permission and scope; email/ad are review-only
platformsOptional non-empty list; posts support ig, fb, th, in, x, bsky; stories support ig/fb; reels accept exactly one of ig/fb
publish_atAuto only: now or a future ISO timestamp with a UTC offset, within 30 days; omitted uses the brand's next suitable slot
expire_afterPositive duration such as 30m, 6h or 2d, at most 30d; must end after publish_at
idempotency_keyOptional but strongly recommended; non-empty, at most 128 characters
imagesObjects containing exactly one url or data_url; post/email/ad accept 1, story/reel accept 4
email_formatEmail only: standard, leaflet_tiles, leaflet_image or auto
metadataUp to 20 string fields, keys up to 64 characters, values up to 500; encoded object up to 4,000 characters

Unknown fields are rejected. Channel selection is exact: an unavailable selected channel never broadens the request to other channels. Omitted platforms snapshot the connected, permitted channels at acceptance; key destination restrictions narrow this set. Reels choose the first eligible destination in the app's channel order. Channels connected later do not silently join an accepted request. A review request with no available destination can still generate downloadable content; an auto request with no available destination returns 409. Actual outputs and omitted channels appear in deliveries and skipped_platforms after generation.

Validation uses the same body, grants and destination rules. It returns cost, wallet affordability and currently available destinations. It does not fetch images, upload, reserve tokens, generate or publish. A valid result cannot guarantee later channel availability or model success.

Images and billing

The entire JSON body must fit within 20 MiB, including base64 expansion. Use public HTTP(S) image URLs for larger inputs. Each decoded/fetched input is capped at the configured upload limit (15 MiB by default) and normalized by the shared raster decoder. Supported raster formats include PNG, JPEG, WebP, HEIC/HEIF, AVIF, GIF, TIFF and BMP; SVG is not accepted. Image fetches validate every redirect and DNS address before connecting. Private-network URLs and credentialed URLs are refused.

KindCreative tokens
post1
story2
reel5
email1
ad1

A request pays for one creative, including its platform variants. Text-only caption edits are free. Regeneration creates a new review-mode request at the format's normal cost, preserving the original; it is not an in-place image or frame reroll. The regeneration endpoint requires a new brief and idempotency key. Frame-level editing and email layout editing remain in the app.

Generation failure or confirmed enqueue failure releases the reservation. Expiring/cancelling already-generated content and publishing failure do not refund successful generation. A network error or HTTP 500 is not proof that nothing was charged: retry with the same idempotency key and inspect the stored request. Interrupted jobs are reconciled without rerunning paid generation; already-produced content is linked, and confirmed stranded work is failed/refunded. Test brands may generate placeholders without tokens or live provider calls.

Requests and current status

StatusMeaning
composingGeneration is queued or running
awaiting_reviewAt least one finished draft needs review; email/ad drafts also use this state
scheduledAt least one delivery is scheduled or publishing
partially_publishedAt least one channel published and another failed, is pending, or had no output
publishedAll requested destinations published; none were skipped
blockedA delivery needs channel reconnection or resumption
failedGeneration or delivery failed; inspect safe error codes
expiredThe deadline passed; no pending delivery may start
cancelledRequest or remaining draft was cancelled

GET /requests/{id} returns current truth across all platform cards. deliveries includes each content id, platform, generation/review/delivery status, timestamps, external id, safe error and action required. In-flight deliveries use the per-delivery status publishing. Partial publication may coexist with pending work or failures. Undoing approval can change scheduled back to awaiting_review.

complete means every delivery reached a terminal publication outcome (published, failed, expired or cancelled); a review-mode draft is ready to retrieve while complete is false. Omitted/unavailable destinations count as finished omissions; a partly published request can therefore be complete with skipped_platforms. To wait for generation, check the delivery generation_status fields rather than waiting for complete. Look at each delivery when a request is partially published. skipped_platforms reports requested channels without an output card; it is populated once content exists.

Deadlines and cancellation are checked when arming and again before provider send. A provider call already in flight may finish after cancellation/deadline; completed publications remain reported as published. Cancellation cannot retract a live post. The minute housekeeping process withdraws eligible siblings, even when another sibling already published.

Idempotency is scoped to the key family and survives rotation. Repeating a stored key returns HTTP 200 with the original request, even after publish_at has passed; a different body does not replace it. Replay uses read quota. Keep the same key for transport retries; use a different key for intentionally new work. Failed stored requests also replay their existing outcome.

Endpoint reference

All paths below are relative to /api/v1. List endpoints use limit (1–100, default 25) and an opaque cursor, returning data and next_cursor. Pass the returned cursor unchanged. Lists are newest first with stable id ordering within equal timestamps. Requests list is history across the brand; idempotency_key lookup is restricted to the calling key family.

Method and pathPurpose
POST /content/validatePreflight a creation body
POST /contentQueue generation
GET /contentList content; optional kind and platform filters
GET /content/{id}Caption, headline, ordered story/email blocks, media, format details, lifecycle and review link
PATCH /content/{id}Set caption on a ready, unpublished post/story/ad; reject over-limit text
POST /content/{id}/actionsapprove, schedule, publish, cancel or restore a social draft
POST /content/{id}/regenerateNew review-mode generation; body: brief and idempotency_key
GET /requestsRequest history; optional idempotency_key lookup
GET /requests/{id}Aggregate and per-delivery state
POST /requests/{id}/cancelStop remaining work; cannot recall an in-flight send
GET /channelsCurrent availability, permissions, format and caption capabilities
GET /libraryBrand media ids and download URLs
GET /calendarApp calendar; required from/to dates in YYYY-MM-DD, inclusive brand-timezone range up to 90 days
GET /metricsCached channel rollups; optional comma-separated content_ids (up to 100) for content metrics
GET /walletToken balance and plan
GET /usageToken ledger: positive tokens consume, negative tokens grant/refund, zero records a free edit
GET /keyCalling key's current policy
PATCH /keyReduce scopes, destinations, publishing grant or daily token ceiling
POST /key/rotateMint replacement; optional grace_seconds (0–86400, default 3600)
GET /webhooksList subscriptions
POST /webhooksCreate subscription with url; reveal signing_secret once
PATCH /webhooks/{id}Enable/disable subscription
DELETE /webhooks/{id}Delete subscription and its delivery history
GET /webhooks/{id}/deliveriesPaginated attempts and safe failure details
POST /webhooks/{id}/deliveries/{deliveryId}/replayRetry an undelivered, idle event

For actions, send {"action":"schedule","publish_at":"2026-12-01T09:00:00Z"} using a future date within the permitted horizon. Approve chooses the normal brand slot; publish starts now. Cancellation disarms the individual card; restore returns it to review. A cancelled or expired request cannot be reactivated through a card action: create a fresh request. Uncertain provider sends return 409 and require verification in the app before any retry.

Content media URLs are signed for five minutes; fetch the content again for fresh URLs. URL can be null when media is unavailable. Library returns user-facing media only. Metrics are cached, may be missing, and carry their collection timestamps; reading never forces a provider refresh. Calendar follows the app's field names and includes items, coverage and meta. Channel connected means currently eligible for publishing; a paused or disconnected channel is false.

Key rotation and limits

Rotation returns plaintext once. Store the replacement before the old key's grace period ends. A second attempt to rotate the expiring predecessor returns 409; the plaintext cannot be recovered after a lost response. Revoke that family and create a replacement from Settings if necessary. Rotation shares capacity, quotas, idempotency and token accounting with its predecessor. Revoking any family member in Settings revokes the family. Queued work is governed by its accepted request; cancelling a request is how to stop it.

LimitProStudio
Active key families per brand15
Writes per rolling 24 hours per family100500
Write burst per rolling minute per family1010
Reads per rolling minute per family6060

Validation uses read quota. Mutations use write quota. A daily_token_limit additionally caps net API creative token usage over a rolling 24-hour window for the family; rotation cannot reset it. Rate-limit responses include Retry-After. Limiters and accepted request state are shared across web replicas.

Webhook delivery

Create up to five subscriptions per brand with a public HTTPS URL. Subscriptions receive request.updated snapshots while enabled. A minute worker observes state changes; rapid intermediate transitions can coalesce into the latest snapshot. Each event contains id, type, created_at, request_id, monotonically increasing request version and data (the request representation). Existing requests may produce an initial snapshot after subscribing.

Delivery is at least once. Persist the event id and ignore duplicates. Request versions let consumers discard stale/out-of-order snapshots. Responses with a 2xx status acknowledge the event; redirects are refused. Network errors, oversized responses and other statuses retry, up to eight attempts with exponential delays starting at 30 seconds; the minute poll determines actual delivery time. Disabled subscriptions pause pending delivery. Successful deliveries cannot be manually replayed; undelivered idle ones can. Keep handlers quick: requests time out after ten seconds and responses are capped at 64 KiB.

The Chaviar-Event-Id header contains the stable event id. Chaviar-Signature is t=<unix-seconds>,v1=<hex-hmac>. Verify HMAC-SHA256 using the secret and exactly timestamp + "." + raw_request_body. Compare signatures in constant time, reject stale timestamps (for example outside five minutes), then parse JSON and process/deduplicate the event. Every attempt gets a fresh timestamp/signature but keeps the event id. The example client includes a verification helper. Store secrets securely; list endpoints never reveal them.

Errors and retry handling

Errors use {"error":{"code":"...","message":"..."}}. Responses include X-Request-Id and Cache-Control: no-store. Branch on error.code, not the text. An error can include a request_id referring to accepted work; the X-Request-Id is the HTTP correlation id.

HTTPCodes
400invalid_request
401unauthorized
402insufficient_tokens
403key_revoked, key_expired, plan_required, kind_not_allowed, auto_not_allowed, scope_required
404not_found
405method_not_allowed
409conflict, destination_unavailable, not_supported
413payload_too_large
415unsupported_media_type
429rate_limited, spend_limit
500internal

Generation/request outcomes use sanitized codes such as generation_failed, content_blocked, publish_failed, schedule_failed, interrupted, enqueue_failed, invalid_image, insufficient_tokens, budget_exceeded and nothing_to_generate. Raw provider errors, prompts, credentials, storage keys and infrastructure costs are not part of this API. Treat future additional fields, statuses and error codes defensively.

© 2026 Chaviar. All rights reserved.

Privacy Policy Terms of Service

Chaviar is a product of Oglofus Ltd, a company registered in England and Wales (company no. 14840351). Registered office: 25 Easten Terrace, Wallsend, NE28 0JW.