https://app.etho.net). All responses set Cache-Control: no-store.
Create a job
POST /api/v1/ad-copy-generations returns 202 with a job and a Location of /api/v1/ad-copy-generations/{id}.
Required JSON:
platform:facebookorinstagramawareness_level:unaware,problem_aware, orsolution_awarebrief: trimmed, 1 to 280 characters
offer_id: an active offer fromGET /api/v1/offerscopy_length:short,medium,long,extra_long, orblog(defaultmedium).blogis Facebook only.
application/json, at most 32 KiB, with exactly one Idempotency-Key (^[A-Za-z0-9._:-]{1,128}$). Use a UUID per intended job. Keys never expire.
Same key and same canonical body replay the current job (202). Same key and a different body return idempotency_mismatch.
Poll a job
GET /api/v1/ad-copy-generations/{id} returns 200 for queued, running, succeeded, and failed. A failed generation is still 200 with status=failed and a safe error.
Poll until terminal. There are no completion webhooks in this preview.
The job never echoes brief or grounding. A successful result has headline, primary_text, optional description, artifact_id, app_url, and optional sources: the ids of the workspace knowledge files the brand brief drew on ([{ "file_id": "…" }]). The brief text itself is never returned and is removed from the job once it finishes. Later edits in the Olympus UI do not change that snapshot.
List jobs
GET /api/v1/ad-copy-generations returns newest first. Default page size 20, maximum 100. Pass cursor from next_cursor. Optional status filter.
List offers
GET /api/v1/offers returns active offers as {id, object:"offer", name} so you can fill offer_id. Archived and other-workspace offers are omitted.
Idempotency
The server hashes the key and the canonical request (sorted keys, defaults applied). The first accepted unique key consumes one monthly allocation unit even if generation later fails. Authentication, validation, entitlement, quota rejection, and replay consume none.Errors
Failures useapplication/problem+json with type, title, status, detail, and request_id. type URIs are https://docs.getolympus.com/problems/{code}.
invalid_request
https://docs.getolympus.com/problems/invalid_request (400). Malformed JSON, unknown fields, missing Idempotency-Key, oversize body, Content-Encoding, wrong Content-Type, or a bad cursor.
invalid_auth
https://docs.getolympus.com/problems/invalid_auth (401) plus WWW-Authenticate: Bearer. Missing, malformed, revoked, unbound, ola_, or olr_ credentials.
forbidden
https://docs.getolympus.com/problems/forbidden (403). The token’s role cannot perform the action (Viewer create), or membership was removed.
preview_disabled
https://docs.getolympus.com/problems/preview_disabled (403). The workspace has no enabled public API access row, or allocation is unset.
workspace_frozen
https://docs.getolympus.com/problems/workspace_frozen (403). Billing freeze. Reads and writes are blocked.
not_found
https://docs.getolympus.com/problems/not_found (404). Unknown job id, or an offer that is archived or belongs to another workspace. The response does not disclose which.
missing_brand
https://docs.getolympus.com/problems/missing_brand (422). The workspace has no Brand Codex.
grounding_too_large
https://docs.getolympus.com/problems/grounding_too_large (422). Trusted Brand/offer/avatar/Think facts exceed 16 KiB per field or 128 KiB total. Nothing is truncated.
idempotency_mismatch
https://docs.getolympus.com/problems/idempotency_mismatch (422). The key was already used with a different body.
quota_exceeded
https://docs.getolympus.com/problems/quota_exceeded (429) plus Retry-After and reset_at (start of next UTC month).
rate_limited
https://docs.getolympus.com/problems/rate_limited (429) plus Retry-After. Per-instance limiter on the token or on malformed-auth IP.
unavailable
https://docs.getolympus.com/problems/unavailable (503). Global PUBLIC_API_ENABLED is off for mutations, or an internal dependency is down. GET still works when the flag is off.