# Audome — full documentation > Generated from https://audome.io/docs. All pages concatenated for easy LLM ingestion. > Last generated: 2026-05-20T19:55:09.778Z Sources: - Audome Documentation: https://audome.io/docs - Getting started with Audome: https://audome.io/docs/getting-started - POST /api/render/generate: https://audome.io/docs/api/render - POST /api/render/preview: https://audome.io/docs/api/preview - Audome Embed SDK: https://audome.io/docs/embed - Audome Webhooks: https://audome.io/docs/webhooks --- # Audome Documentation Audome is a programmatic image and video render platform. You design a template once in the Studio, then call our REST API or embed the Studio inside your own app to generate thousands of personalised assets. This documentation is fully public and intentionally machine-readable so ChatGPT, Claude, Cursor, and other AI agents can ingest it directly. ## Sections - [Getting started](/docs/getting-started) — embed snippet and your first render call. - [POST /api/render/generate](/docs/api/render) — server-side render. - [POST /api/render/preview](/docs/api/preview) — realtime preview. - [Embed SDK](/docs/embed) — `AudomeEmbed.studio` JavaScript SDK reference. - [Webhooks](/docs/webhooks) — how to receive completed render notifications. ## Machine-readable feeds - `https://audome.io/llms.txt` — index of all docs for LLM crawlers. - `https://audome.io/llms-full.txt` — the entire corpus as a single markdown file. - `https://audome.io/sitemap.xml` — XML sitemap for search engines. - `https://audome.io/docs/raw/.md` — raw markdown for any page. ## What is Audome? Audome (audome.io) is a Saudi-built rendering platform optimised for high-volume creative automation. The product has three surfaces: 1. **Studio** — a visual editor at `/render` for designing templates with dynamic text and image slots. 2. **REST API** — JSON endpoints under `/api/render/*` that accept a template id plus dynamic values and return a rendered PNG/JPEG/WebP. 3. **Embed SDK** — a single ` ``` See [Embed SDK](/docs/embed) for the full options reference and the `postMessage` event protocol. ## 5. Production checklist - Move tokens out of client-side code and into your server. - Sign embed sessions with short-lived tokens that you proxy from your backend, so customers never see your master key. - Cache rendered URLs — they are immutable and served from CDN. - Set up a [webhook](/docs/webhooks) for asynchronous workflows so you do not have to keep the HTTP connection open for slow renders. --- # POST /api/render/generate Render a template synchronously and return a public CDN URL to the finished image. This is the workhorse endpoint of the Audome API. ```http POST https://audome.io/api/render/generate Authorization: Bearer YOUR_API_TOKEN Content-Type: application/json ``` ## Request body | Field | Type | Required | Description | | ---------------- | ------- | -------- | --------------------------------------------------------------------------- | | `projectId` | string | yes\* | Audome template ID. Either `projectId` or `html` must be provided. | | `data` | object | no | Dynamic values keyed as `.text` or `.img`. | | `html` | string | yes\* | Inline HTML to render. Mutually exclusive with `projectId`. | | `width` | integer | no | Canvas width in pixels. Defaults to the template width. | | `height` | integer | no | Canvas height in pixels. Defaults to the template height. | | `format` | enum | no | One of `png`, `jpeg`, `webp`. Defaults to `png`. | | `quality` | integer | no | 1-100. Ignored for `png`. Defaults to 90. | | `backgroundColor` | string | no | CSS colour. Use `transparent` to force PNG with alpha. | \* Either `projectId` or `html` is required. ### Dynamic data shape Each editable element in your template has a `dynamicKey`. Provide values namespaced by suffix: - `.text` for text elements. - `.img` for image elements (must be a public HTTPS URL). ```json { "projectId": "8fce2c7e-7e6f-4d46-b5b1-2aaa5a44f1c9", "data": { "title.text": "Summer sale", "subtitle.text": "Up to 50% off", "hero.img": "https://images.example.com/hero.jpg" } } ``` ## Successful response ```json { "success": true, "imageUrl": "https://cdn.audome.io/renders/2026/05/abc123.png", "width": 1080, "height": 1080, "format": "png", "creditsUsed": 1, "creditsRemaining": 499, "generationId": "gen_01HX9Y..." } ``` ## Errors ```json { "success": false, "error": "Insufficient credits", "code": "INSUFFICIENT_CREDITS" } ``` | Status | Code | Meaning | | ------ | ---------------------- | ---------------------------------------------------- | | 400 | `VALIDATION_FAILED` | Body did not validate against the schema. | | 401 | `UNAUTHORIZED` | Missing or invalid Bearer token. | | 402 | `INSUFFICIENT_CREDITS` | Account has zero render credits. | | 404 | `PROJECT_NOT_FOUND` | `projectId` does not exist for this account. | | 429 | `RATE_LIMITED` | Per-minute request limit exceeded. | | 500 | `RENDER_FAILED` | Internal renderer error. Safe to retry once. | ## Examples ### cURL ```bash curl -X POST https://audome.io/api/render/generate \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "projectId": "PROJECT_ID", "data": { "title.text": "Hello" } }' ``` ### Node.js ```js const res = await fetch('https://audome.io/api/render/generate', { method: 'POST', headers: { Authorization: `Bearer ${process.env.AUDOME_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ projectId: 'PROJECT_ID', data: { 'title.text': 'Hello world' }, format: 'jpeg', quality: 85, }), }); const json = await res.json(); console.log(json.imageUrl); ``` ### Python ```python import os, requests r = requests.post( "https://audome.io/api/render/generate", headers={"Authorization": f"Bearer {os.environ['AUDOME_TOKEN']}"}, json={ "projectId": "PROJECT_ID", "data": {"title.text": "Hello world"}, "format": "png", }, timeout=60, ) r.raise_for_status() print(r.json()["imageUrl"]) ``` ## Discovering a project's dynamic schema To programmatically learn which keys a template accepts: ```bash curl https://audome.io/api/render/projects/PROJECT_ID \ -H "Authorization: Bearer YOUR_API_TOKEN" ``` The response includes a `dynamicElements` array, each with `type`, `dynamicKey`, `defaultValue`, and `placeholder`. --- # POST /api/render/preview Realtime, low-latency preview endpoint. Use it for live editors where a human (or an LLM) is iteratively tuning a template and you want sub- second visual feedback before committing to a paid render. > Previews are **free** — they never consume render credits. They are > encoded as WebP at quality 70 and cached for one hour by content hash. > Pixel-identical to `/generate` (no watermark, no overlay). ```http POST https://audome.io/api/render/preview Authorization: Bearer YOUR_API_TOKEN Content-Type: application/json ``` ## Request body | Field | Type | Required | Description | | ---------------- | ------- | -------- | ------------------------------------------------------ | | `projectId` | string | yes | The template ID returned when you save a project. | | `data` | object | no | Dynamic values; same flat `"key.text"`/`"key.img"` shape as `/generate`. | ## Response ```json { "success": true, "previewUrl": "https://audome.io/api/images/renders//preview-.webp", "expiresAt": 1746640000000, "watermarked": false, "lowRes": true, "cached": false, "rateLimit": { "remaining": 29, "resetAt": 1746640060000 }, "generationTime": 1240 } ``` | Field | Type | Description | | ----------------- | -------- | -------------------------------------------------------------------- | | `previewUrl` | string | CDN URL to a WebP image. Drop into ``. | | `expiresAt` | number | Unix ms when the cached entry expires (1 h after creation). | | `watermarked` | boolean | Always `false` — preview output is not watermarked. | | `lowRes` | boolean | Reduced quality vs `/generate` (WebP @ Q70 instead of full PNG). | | `cached` | boolean | `true` if the result was served from the in-memory cache. | | `rateLimit` | object | `remaining` calls in the active minute window + `resetAt` Unix ms. | | `generationTime`| number | Server-side render time in ms (omitted on cache hit). | ## Caching The cache key is `sha256(projectId + canonical(data) + editor_data.updated_at + WxH)`. `canonical(data)` recursively sorts object keys, so `{ a:1, b:2 }` and `{ b:2, a:1 }` produce identical cache keys. - TTL: **1 hour** per entry. - Capacity: **1 000** entries (LRU eviction). - Cache invalidates automatically when the master template is saved (the `updated_at` column of `render_projects` participates in the key). - Cache hits return in **< 50 ms**. Cache misses take **800-2000 ms**. ## Rate limits | Window | Limit | | ------- | ------------ | | 1 min | 30 requests | | 1 hour | 200 requests | Token-bucket algorithm, per Audome account. When you exceed either limit the response is `429`: ```json { "error": "rate_limited", "retryAfter": 12, "rateLimit": { "remaining": 0, "resetAt": 1746640060000 } } ``` The response also sets these headers: ```http Retry-After: 12 X-RateLimit-Limit-Minute: 30 X-RateLimit-Limit-Hour: 200 X-RateLimit-Reset: 1746640060000 ``` ## Concurrency: previews never block paid renders Previews are processed by a separate Puppeteer queue capped at `PREVIEW_MAX_CONCURRENCY` (default `1`). The queue runs **next to** `/api/render/generate` and never holds a render slot, so a flood of preview requests cannot delay a paying customer's final render. If the preview queue is saturated the request waits in the queue (up to 30 s) before timing out with `503`. ## Differences vs /api/render/generate | Aspect | `/preview` | `/generate` | | ------------------- | ------------------------- | --------------------------- | | Credits charged | 0 | 1 per call | | Output format | WebP @ Q70 | PNG / JPEG / WebP | | Output storage | CDN (1 h TTL) | CDN (permanent) | | Watermark | never | never | | Cache | content-hashed, 1 h | none | | Latency target | < 50 ms cached, 1-2 s cold| 1.5 - 4 s | | Webhook supported | no | yes | | Rate limit | 30/min, 200/hour | account-wide 120/min | ## Recommended client behavior 1. **Debounce 500-800 ms** between user input and the preview call. Avoid firing a request on every keystroke. 2. **Hash the payload locally** and skip the network call entirely if it is identical to the last in-flight request. 3. **Use `AbortController`** to cancel an in-flight request when the user keeps typing — the server caches the eventual result, but you do not want stale UI. 4. **Show the rate limit**: when `rateLimit.remaining < 5` consider pausing previews and falling back to "click to preview". ## Example: live editor wiring ```js const debounceMs = 600; let timer; let inflight; async function schedulePreview(state) { clearTimeout(timer); if (inflight) inflight.abort(); timer = setTimeout(async () => { inflight = new AbortController(); try { const res = await fetch('https://audome.io/api/render/preview', { method: 'POST', signal: inflight.signal, headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ projectId: state.projectId, data: state.data, }), }); if (res.status === 429) { const { retryAfter } = await res.json(); console.warn(`Rate limited, retry in ${retryAfter}s`); return; } const { previewUrl, cached, rateLimit } = await res.json(); document.querySelector('#preview').src = previewUrl; if (rateLimit.remaining < 5) { // pause auto-preview to avoid hitting the limit } } catch (e) { if (e.name !== 'AbortError') throw e; } }, debounceMs); } ``` ## Error codes | Status | `error` value | Meaning | | ------ | --------------------- | ---------------------------------------------------- | | 400 | `missing_projectId` | Body did not include `projectId`. | | 401 | `unauthorized` | Bearer token missing or invalid. | | 403 | `forbidden` | Token does not own `projectId`. | | 404 | `project_not_found` | The template ID does not exist or was deleted. | | 429 | `rate_limited` | Account exceeded preview rate limits. | | 503 | `preview_busy` | Preview queue saturated. Retry after `retryAfter`. | | 500 | `render_failed` | Internal error. Includes `requestId` for support. | ## Notes for AI agents wiring this up - This endpoint is **not a substitute for `/generate`** when you need a permanent, full-resolution asset. Preview output is lower-resolution WebP @ Q70 with a 1-hour CDN TTL. Use `/generate` once the user confirms the design. - The `cached: true` field lets you skip showing a loading spinner on fast paths — treat it as "this came from L1 cache". - The `generationTime` field is useful telemetry for monitoring the render pool's warmth. --- # Embed SDK reference Drop the Audome Studio inside any web app with a single ` ``` The script is hosted on Audome's CDN, ~12 KB gzipped, has zero dependencies, and is safe to ship in production. ## Minimal example ```html
``` ## Configuration ```ts interface AudomeStudioConfig { container: string | HTMLElement; token: string; theme?: 'dark' | 'light'; // default 'dark' locale?: 'ar' | 'en'; // default browser language height?: string; // CSS height, default '700px' categoryId?: string; // restrict project picker to one category projectId?: string; // open directly on a single project readOnly?: boolean; // hide save/export buttons onReady?: () => void; onProjectCreate?: (project: { id: string; name: string }) => void; onProjectSave?: (project: { id: string; name: string }) => void; onRender?: (imageUrl: string) => void; onFontUpload?: (font: { id: string; familyName: string }) => void; onError?: (error: { code: string; message: string }) => void; } ``` ## Returned handle `AudomeEmbed.studio()` returns an object you can use to control the embed after mount: ```ts interface AudomeStudioHandle { destroy(): void; reload(): void; setLocale(locale: 'ar' | 'en'): void; setTheme(theme: 'dark' | 'light'): void; openProject(projectId: string): void; exportImage(opts?: { format?: 'png' | 'jpeg' | 'webp' }): Promise; } ``` ## postMessage event protocol Internally the SDK uses an iframe and `postMessage`. Every event is JSON of the shape `{ source: 'audome', type: string, payload: any }`. Listen yourself if you need finer control: ```js window.addEventListener('message', (event) => { if (event.origin !== 'https://audome.io') return; if (event.data?.source !== 'audome') return; switch (event.data.type) { case 'studio:ready': /* iframe loaded */ break; case 'project:create': /* { id, name } */ break; case 'project:save': /* { id, name } */ break; case 'render:complete': /* { imageUrl } */ break; case 'render:error': /* { code, message } */ break; case 'font:upload': /* { id, familyName } */ break; } }); ``` ## React example ```tsx import { useEffect, useRef } from 'react'; export function AudomeStudio({ token }: { token: string }) { const ref = useRef(null); const handle = useRef(null); useEffect(() => { const script = document.createElement('script'); script.src = 'https://audome.io/embed/audome-embed.js'; script.onload = () => { handle.current = (window as any).AudomeEmbed.studio({ container: ref.current, token, theme: 'dark', onRender: (imageUrl: string) => console.log('rendered', imageUrl), }); }; document.body.appendChild(script); return () => { handle.current?.destroy(); script.remove(); }; }, [token]); return
; } ``` ## Security tips - Never ship a long-lived master token to the browser. Mint short- lived per-customer tokens server-side and pass those to the embed. - Use `categoryId` to restrict which projects a customer sees. - Set a strict Content Security Policy that allows `frame-src https://audome.io` so the iframe can mount. --- # Webhooks For long-running renders or batch jobs you can register a webhook URL that Audome calls when each generation completes. Webhooks save you from holding open HTTP connections and let you build async pipelines. ## Configuring a webhook 1. Go to `https://audome.io/render/api-tokens`. 2. Click **Webhooks** next to a token. 3. Add a destination URL (must be HTTPS) and a secret. You can register up to 5 webhook destinations per account. ## Payload Every webhook delivery is a `POST` with body: ```json { "id": "evt_01HX9Y...", "type": "render.completed", "createdAt": "2026-05-07T12:34:56.000Z", "data": { "generationId": "gen_01HX9Y...", "projectId": "8fce2c7e-...", "imageUrl": "https://cdn.audome.io/renders/2026/05/abc.png", "width": 1080, "height": 1080, "format": "png", "creditsUsed": 1, "metadata": { /* whatever you passed at request time */ } } } ``` Other event types: - `render.completed` — render succeeded. - `render.failed` — render permanently failed. - `token.created` — a new API token was minted on the account. - `token.revoked` — a token was deleted. ## Verifying signatures Each delivery includes: ```http Audome-Signature: t=1715098496,v1= Audome-Event: render.completed ``` Compute the HMAC of `.` with your webhook secret: ```js import crypto from 'node:crypto'; function verify(rawBody, header, secret) { const [tPart, sigPart] = header.split(','); const t = tPart.split('=')[1]; const expected = crypto .createHmac('sha256', secret) .update(`${t}.${rawBody}`) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(expected), Buffer.from(sigPart.split('=')[1]) ); } ``` Reject any request older than 5 minutes to prevent replay attacks. ## Retries Audome retries non-2xx responses with exponential backoff for up to 24 hours: 1 min, 5 min, 30 min, 2 h, 6 h, 24 h. After that the event is marked `failed` and visible in the dashboard's webhook log. ## Local testing Use any tunnel (ngrok, cloudflared, tailscale funnel) to expose your local server, then send a test event from `https://audome.io/render/api-tokens` -> **Webhooks** -> **Send test**.