Generation API
Integrate the most capable frontend design engine directly into your applications, CI pipelines, or internal tools.
Architecture Decision: REST API vs MCP
AIDesigner exposes two programmatic surfaces. Choose the REST API when your own service should make HTTP requests. Choose MCP when a coding assistant should call AIDesigner directly through OAuth and tool use.
REST API
Language-agnostic HTTP endpoints for backend services, queued jobs, CI, or internal tools.
- Authenticate with API keys
- Works from any runtime
- Best when you own the request lifecycle
MCP Server
OAuth-backed tool integration for Claude Code, Codex, Cursor, VS Code, and Windsurf.
- No API key handling in your app
- Native assistant tool calling
- Best for repo-aware interactive workflows
Authentication
The REST API uses bearer authentication. Provision API keys from Settings → API Keys, copy the full secret when it is created, and send it in the Authorization header for each request.
Authorization: Bearer gp_your_secret_api_keyNever expose secret keys in client-side code. If you want your assistant to authenticate directly, use MCP and OAuth instead of embedding API keys.
Rate Limits
REST generation traffic is limited per authenticated account. API keys created under the same account share one REST budget, which prevents key rotation from bypassing the cap.
| Limit | Default | Scope |
|---|---|---|
| Generation requests | 30 / 60s | Shared across all API keys on the same account |
| Concurrent generations | 4 in flight | Shared across all API keys on the same account |
Successful requests include standard rate-limit headers so callers can back off proactively. When the limit is exceeded, the API returns 429 Too Many Requests with a Retry-After header.
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 27
X-RateLimit-Reset: 1775476860
X-RateLimit-Window: 60
X-RateLimit-Policy: 30;w=60
X-RateLimit-Concurrency-Limit: 4/generateDesign
/api/v1/generateDesignGenerates a single-file HTML interface from either a prompt or a multi-turn message history. Website analysis can be enabled by passing a mode and source URL.
Request Body
prompt Required unless messages is provided | string Single-turn natural language description of the interface you want to generate. |
messages Required unless prompt is provided | array Conversation history for iterative refinement and multi-step design generation. |
streaming Optional · Default true | boolean Return OpenAI-style server-sent event chunks when true, or a single JSON payload when false. |
mode Optional | enum Website analysis mode: inspire, clone, or enhance. |
url Required when mode is set | string Reference URL to scrape and analyze before generation. |
session Optional · "new" or a session UUID | string Opt in to session-aware delivery. Pass "new" to create a session for this run, or an existing session_id to generate into it. Omit entirely and the endpoint behaves exactly as documented above — no headers, no extra response fields. |
canvas_id Optional · Requires session | string Refine an existing canvas in place instead of creating a new one. Must belong to the session and requires session to also be set, or the request 400s. |
idempotency_key Optional · 1-128 chars · Requires session | string Scoped per account. Replaying the same key returns the original run's result instead of generating and billing again. |
design_mode Optional · Default classic | enum classic or ultradesign. Any other value 400s. See Ultradesign below for what the premium mode adds and what it costs. |
viewport Optional · Default desktop | enum desktop or mobile. mobile switches ultradesign to an app-style, multi-screen output. Strictly validated — unrecognized values 400 instead of being silently ignored. |
See Canvas Delivery for the response headers, streaming event, and canvas_delivery values these params add.
curl -X POST https://api.aidesigner.ai/api/v1/generateDesign \
-H "Authorization: Bearer $AIDESIGNER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A modern dashboard with account balances, charts, and recent activity",
"streaming": false
}'Sessions
Sessions attach generations to a real editor project instead of returning raw HTML you have to store yourself. Create a session once, then pass its ID to /generateDesign to build up a canvas history you can reopen in the editor, refine in place, and list back out through the API.
Create a session
/api/v1/sessionstitle Optional · Max 120 characters | string Human-readable name shown in the editor sidebar and session list. Omit it to create an untitled session. |
Returns 201 with the new session's ID, editor URL, title, and creation time.
{
"session_id": "1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c",
"editor_url": "https://app.aidesigner.ai/editor/1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c",
"title": "API example session",
"created_at": "2026-07-27T18:04:11.000Z"
}List sessions
/api/v1/sessionslimit Optional query param · 1-100 · Default 20 | integer Maximum number of sessions to return, most recently updated first. |
cursor Optional query param | string Pass the previous page's next_cursor to continue pagination. |
{
"sessions": [
{
"session_id": "1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c",
"title": "API example session",
"editor_url": "https://app.aidesigner.ai/editor/1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c",
"canvas_count": 2,
"published_url": null,
"created_at": "2026-07-27T18:04:11.000Z",
"updated_at": "2026-07-27T18:06:42.000Z"
}
],
"next_cursor": null
}Get a session
/api/v1/sessions/:idid Path parameter · UUID | string The session_id returned when the session was created. |
Adds a canvases array to the session fields above — one entry per canvas in the session, without the HTML body.
{
"session_id": "1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c",
"title": "API example session",
"editor_url": "https://app.aidesigner.ai/editor/1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c",
"created_at": "2026-07-27T18:04:11.000Z",
"updated_at": "2026-07-27T18:06:42.000Z",
"canvases": [
{
"canvas_id": "9e21d4f0-6c3b-4a2d-8f1e-0d9c8b7a6f5e",
"name": "Coffee landing page",
"kind": "design",
"status": "complete",
"version": 2,
"updated_at": "2026-07-27T18:06:42.000Z"
}
]
}Get a canvas
/api/v1/sessions/:id/canvases/:canvasIdid Path parameter · UUID | string The session the canvas belongs to. |
canvasId Path parameter · UUID | string The canvas_id returned by /generateDesign or the session detail endpoint above. |
Same shape as one entry in canvases above, plus the full html for that canvas.
curl -X POST https://api.aidesigner.ai/api/v1/sessions \
-H "Authorization: Bearer $AIDESIGNER_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "title": "API example session" }'Publishing
Publish every canvas in a session to a live *.aidesigner.site site. The first publish auto-generates a random subdomain if you don't already have one claimed for the session; republishing reuses it. All five endpoints below sit under /api/v1/sessions/:id/publish or /api/v1/subdomains and use the same bearer auth as every other endpoint on this page.
Free-tier publishes are watermarked — the response's watermark field reflects your subscription at publish time. On a transient lookup failure it conservatively reports true — GET the publish status to re-check if you need to confirm whether the badge actually made it onto the live site. Thumbnails are generated in the background after a publish completes — poll GET .../publish until thumbnail_url is populated instead of expecting it on the publish response itself.
Publish a session
/api/v1/sessions/:id/publishPublishes every canvas currently in the session. Calling it again republishes with the latest canvas contents under the same subdomain.
id Path parameter · UUID | string The session to publish. |
home_page_id Optional · UUID | string Which canvas becomes the site's index page. Defaults to the session's existing home page, or its first canvas if none is set. |
curl -X POST https://api.aidesigner.ai/api/v1/sessions/1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c/publish \
-H "Authorization: Bearer $AIDESIGNER_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'{
"published": true,
"subdomain": "coffee-landing-4f2a",
"published_url": "https://coffee-landing-4f2a.aidesigner.site",
"published_at": "2026-07-27T18:09:03.000Z",
"page_count": 2,
"watermark": true
}Get publish status
/api/v1/sessions/:id/publishid Path parameter · UUID | string The session to check. |
Returns { published: false } if the session has never been published. Once it has, this is also where thumbnail_url shows up after background generation finishes.
{
"published": true,
"subdomain": "coffee-landing-4f2a",
"published_url": "https://coffee-landing-4f2a.aidesigner.site",
"published_at": "2026-07-27T18:09:03.000Z",
"custom_domain": null,
"watermark": true,
"site_title": null,
"site_description": null,
"thumbnail_url": "https://cdn.aidesigner.ai/sites/1b8f3c2a.../thumbnail.webp"
}Unpublish a session
/api/v1/sessions/:id/publishid Path parameter · UUID | string The session to unpublish. |
Takes the site down and releases its subdomain. Idempotent — calling it on a session that isn't published still returns 200, not a 404 or 409.
{
"published": false,
"released_subdomain": "coffee-landing-4f2a",
"removed_custom_domain": null
}Claim a custom subdomain
/api/v1/sessions/:id/publish/subdomainRequires the session to already be published — call publish first.
id Path parameter · UUID | string The already-published session. |
subdomain Required · 3-63 chars | string Lowercase letters, numbers, and hyphens only. Must start and end with a letter or number, and can't contain consecutive hyphens. |
{
"subdomain": "my-coffee-co",
"published_url": "https://my-coffee-co.aidesigner.site"
}Check subdomain availability
/api/v1/subdomains/checksubdomain Required query param · 3-63 chars | string The name to check. |
exclude_session Optional query param · UUID | string Exclude this session's own current subdomain from the taken check, so re-checking a name you already hold reports available. |
An unavailable, taken, or reserved name is a 200 reporting { available: false, reason }, not an HTTP error — that check is about the name, not the request. A malformed request (missing or under 3-character subdomain) still fails validation and returns 400.
curl -G https://api.aidesigner.ai/api/v1/subdomains/check \
-H "Authorization: Bearer $AIDESIGNER_API_KEY" \
--data-urlencode "subdomain=my-coffee-co"{
"available": false,
"reason": "This subdomain is already taken"
}Errors
All five endpoints share one error taxonomy. Bodies look like { error: <code>, message: <string> }. This coded taxonomy applies specifically to publish-flow failures — two other shapes can still show up on these endpoints and aren't part of the table below: request validation failures (bad UUIDs, an out-of-range subdomain, etc.) return { error: "Invalid input", details } with Zod's flattened issue tree, and rate-limit rejections use the rate-limit error shape (type: "rate_limit_exceeded" plus a Retry-After header) described under Rate limits below.
| Status | error code | Meaning |
|---|---|---|
| 404 | session_not_found | The session doesn't exist or doesn't belong to your account. |
| 422 | nothing_to_publish | The session has no canvases yet — generate at least one before publishing. |
| 422 | content_blocked | Moderation rejected the publish — the canvas content appears to impersonate a real brand or uses a disallowed full-screen wrapper pattern. The message field explains which check failed. |
| 409 | not_published | A subdomain operation was attempted on a session that has never been published. |
| 409 | subdomain_taken | Another site already holds that subdomain. Check availability first if you want to avoid this. |
| 400 | invalid_subdomain | The requested subdomain fails validation (length, characters, hyphen rules) or is a reserved name like www or admin. |
| 500 | publish_failed | An unexpected internal failure. The message is intentionally generic — internal details are never included. |
Rate limits
Publish, unpublish, and subdomain claims (writes that mutate the live site — the first two trigger R2 uploads and a background render; a subdomain claim mutates a scarce, shared namespace) share a dedicated, tighter bucket than the rest of the Sessions API: 10 requests / 60s, concurrency 2. Status checks and availability checks use the same 60 requests / 60s session bucket documented under Canvas Delivery. Both buckets return 429 with a Retry-After header when exceeded.
Custom Domains
Point your own domain at a published session instead of *.aidesigner.site. Custom domains require an active PRO subscription on the session's team and a session that's already published — see Publishing first. Four endpoints sit under /api/v1/sessions/:id/domain and a fifth, detection-only endpoint sits at /api/v1/domains/setup.
Attaching, checking, verifying, or removing a domain (every endpoint under /sessions/:id/domain) requires your API key's account to be the session's owner — team members with access to the project can't use these, even if they can open and edit it in the app. GET /api/v1/domains/setup is the one exception: passed a session_id, it's readable by any team member with project access, since it only looks up detection info and never mutates anything.
Connect a domain
/api/v1/sessions/:id/domainRegisters the domain with Cloudflare for both its apex and www variants and saves it against the session. Calling it again with a new domain replaces the previous one.
id Path parameter · UUID | string The published, owned session to attach the domain to. |
domain Required · 4-253 chars | string The domain or subdomain to connect, e.g. example.com or blog.example.com. |
curl -X PUT https://api.aidesigner.ai/api/v1/sessions/1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c/domain \
-H "Authorization: Bearer $AIDESIGNER_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "domain": "example.com" }'{
"domain": "example.com",
"status": "pending",
"dns_target": "sites.aidesigner.ai",
"records": [
{ "type": "CNAME", "host": "www", "value": "sites.aidesigner.ai" },
{ "type": "ALIAS", "host": "@", "value": "sites.aidesigner.ai",
"note": "Use an ALIAS/ANAME record or CNAME flattening for example.com. If your DNS provider supports neither, point www at sites.aidesigner.ai and redirect the apex to www." }
],
"setup": {
"domain": "example.com",
"registrable_domain": "example.com",
"dns_provider": { "id": "cloudflare", "name": "Cloudflare", "dashboard_url": "https://dash.cloudflare.com" },
"domain_connect": {
"provider_id": "cloudflare.com",
"provider_name": "Cloudflare",
"url_sync_ux": "https://dash.cloudflare.com",
"template_available": false,
"one_click_url": null
},
"records": [ "...same as the records array above..." ]
},
"records_zone": "example.com"
}setup is the same detection payload GET /api/v1/domains/setup returns (documented in full below), computed automatically right after the domain is saved. A slow or failing provider lookup must never turn a successful connect into an error response, so on failure setup degrades to null and the top-level records array falls back to the same manual records the detection endpoint would compute — the connection itself still succeeded, and records is always present either way. Handle setup === null by using records directly instead of assuming setup always populates. records_zone names the DNS zone the records array actually describes — for an apex or www input it equals domain; for a subdomain input (blog.example.com) it's the parent registrable domain (example.com) whose zone the returned CNAME records actually belong to. It's null exactly when setup degrades.
Get domain status
/api/v1/sessions/:id/domain{ "configured": false }
// or, once a domain is connected:
{
"configured": true,
"domain": "example.com",
"status": "pending",
"ssl_status": "pending_validation"
}Re-check DNS
/api/v1/sessions/:id/domain/verifyRe-checks DNS for the domain already configured on this session. Returns { error: "no_domain", message: "No custom domain configured" } (400) if nothing is connected yet — this shape is specific to this endpoint and isn't part of the shared error taxonomy below.
{
"domain": "example.com",
"dns_verified": true,
"ssl_status": "active",
"status": "active",
"error": null
}Disconnect a domain
/api/v1/sessions/:id/domainRemoves the domain from Cloudflare and clears it from the session. Returns { success: true } even if no domain was configured.
Detect a provider & get setup instructions
/api/v1/domains/setupIndependent of any session — pass session_id only if you want a one-click apply URL bound to a specific session (it's used solely to scope the ownership verification token; you must still have access to that session). Detection is best-effort — DNS lookups against public resolvers can come back empty for a domain with an unrecognized or slow-to-resolve nameserver setup — so dns_provider and domain_connect can both be null — this is a normal outcome, not an error, and records is always present regardless.
domain Required query param · 4-253 chars | string The domain to inspect. |
session_id Optional query param · UUID | string Scopes one_click_url to this session. Requires read access to the session (owner or team member). |
curl -G https://api.aidesigner.ai/api/v1/domains/setup \
-H "Authorization: Bearer $AIDESIGNER_API_KEY" \
--data-urlencode "domain=example.com" \
--data-urlencode "session_id=1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c"{
"domain": "example.com",
"registrable_domain": "example.com",
"dns_provider": { "id": "cloudflare", "name": "Cloudflare", "dashboard_url": "https://dash.cloudflare.com" },
"domain_connect": {
"provider_id": "cloudflare.com",
"provider_name": "Cloudflare",
"url_sync_ux": "https://dash.cloudflare.com",
"template_available": true,
"one_click_url": "https://dash.cloudflare.com/v2/domainTemplates/providers/aidesigner.ai/services/sites/apply?domain=example.com&redirect_uri=...&sig=...&key=_dck1&state=..."
},
"records": [
{ "type": "CNAME", "host": "www", "value": "sites.aidesigner.ai" },
{ "type": "ALIAS", "host": "@", "value": "sites.aidesigner.ai",
"note": "Use an ALIAS/ANAME record or CNAME flattening for example.com. If your DNS provider supports neither, point www at sites.aidesigner.ai and redirect the apex to www." }
]
}one_click_url semantics
one_click_url is a signed Domain Connect apply URL that, once the caller clicks through their DNS provider's own consent screen, configures DNS automatically — no manual record entry. It is null until AIDesigner's Domain Connect onboarding is complete (a signing key and verification secret configured server-side, and each individual DNS provider having accepted AIDesigner's Domain Connect template — this is a per-provider rollout, not a single global switch) — and it stays null for any domain whose specific provider hasn't onboarded yet, even after that rollout starts. Always treat records as the source of truth and one_click_url as an optional shortcut on top of it — never build a flow that requires one-click to be available.
One-click, when available, operates on the registrable domain — not an arbitrary subdomain — and writes exactly two records into that zone: a www CNAME to sites.aidesigner.ai and a verification TXT record. It does not write anything at the apex. After running one-click, add the apex record manually — an ALIAS/ANAME (or CNAME flattening, or an A record) at @ — or use www with an apex redirect. Connecting blog.example.com still returns manual records only, even once example.com's provider has fully onboarded — for subdomain connections, use the manual records instead. Those records are zone-relative to the registrable domain rather than the input you passed: blog.example.com gets two plain CNAMEs with hosts blog and www.blog (both → sites.aidesigner.ai) rather than @/www, since neither hostname is the zone's literal root — use records_zone to know which zone (example.com) those hosts are relative to.
DNS can't CNAME the apex
A literal CNAME record at a zone apex (the bare domain, with no subdomain) isn't valid DNS. For an apex or www connection, records represents the apex row as type: "ALIAS" with a note covering both ways around the restriction: use your DNS provider's ALIAS/ANAME record type or CNAME flattening if they offer one, or point www at sites.aidesigner.ai and redirect the apex to www if they don't. One-click does not sidestep this restriction today. The template it applies writes only www plus a verification TXT record, so the apex is still yours to set up: after one-click, add the apex record manually (ALIAS/ANAME, CNAME flattening, or an A record) or use www with an apex redirect. (An apex-CNAME template variant that would remove this step exists but is not yet live at any provider.)
Errors
The four /sessions/:id/domain* endpoints share one taxonomy, same { error: <code>, message: <string> } shape as Publishing's. Request-validation failures and rate-limit rejections use the same two shapes documented there too.
| Status | error code | Meaning |
|---|---|---|
| 400 | invalid_domain | The domain fails basic format validation, or it sits on a hosting platform's shared suffix (e.g. myapp.pages.dev) — domains on a hosting platform's shared suffix can't be connected, since that would claim part of a zone you don't actually control. Use a domain whose DNS you own instead. |
| 402 | subscription_required | Custom domains require an active PRO subscription on the session's team. |
| 404 | session_not_found | The session doesn't exist, or your API key's account isn't its owner. |
| 409 | not_published | The session must be published before a domain can be attached. |
| 409 | domain_taken | Another site already has this domain connected. |
| 502 | ssl_provider_error | Registering the domain with the SSL/hosting provider failed. Retry — this is usually transient. |
| 500 | domain_failed | An unexpected internal failure. The message is intentionally generic. |
GET /api/v1/domains/setup shares invalid_domain above and adds one more, specific to detection taking too long:
| Status | error code | Meaning |
|---|---|---|
| 504 | detection_timeout | Provider detection (DNS lookups + Domain Connect discovery) didn't finish within the endpoint's internal 10-second deadline. Retry — this doesn't mean anything is wrong with the domain. |
Rate limits
Connecting and disconnecting a domain (writes that mutate Cloudflare state on a scarce, shared namespace) share Publishing's dedicated write bucket: 10 requests / 60s, concurrency 2. Status checks, DNS re-verification, and provider detection use the standard 60 requests / 60s session bucket.
Canvas Delivery
Requests without session behave exactly as documented in /generateDesign above — no new headers, no extra response fields, nothing to opt out of. Pass session (either "new" or an existing session_id) to opt in: the generated HTML is written onto a canvas in that session as it completes, and the response tells you exactly where it landed.
Opt-in parameters
session "new" or a session UUID | string Create a session for this run ("new") or target an existing one by ID. Required for canvas_id and idempotency_key to take effect. |
canvas_id Requires session | string Refine an existing canvas in place. The server loads its current HTML as the refine base — you never round-trip HTML yourself. |
idempotency_key 1-128 chars · Requires session | string Retry safely. A replayed key returns the original run's result (with idempotent_replay: true in the body) instead of generating and charging credits again. |
Response headers
Set on every opted-in response, before streaming begins:
X-AIDesigner-Session-Id: 1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c
X-AIDesigner-Canvas-Id: 9e21d4f0-6c3b-4a2d-8f1e-0d9c8b7a6f5e
X-AIDesigner-Editor-Url: https://app.aidesigner.ai/editor/1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c
X-AIDesigner-Run-Id: 4a7e9c10-2b1a-4c3d-8e7f-6a5b4c3d2e1fX-AIDesigner-Canvas-Id is only sent once a canvas ID is known at request time — it is omitted on a fresh, non-streaming generation, since that canvas doesn't exist until the run completes. X-AIDesigner-Run-Id is present on non-streaming and idempotent-replay responses only. On a streaming response the run row doesn't exist until after generation finishes, so the run ID arrives inside the final SSE event instead of a header.
Streaming: the aidesigner.result event
When session is set on a streaming request, one named SSE event is written immediately before the stream terminator, carrying everything the non-streaming response returns inline.
event: aidesigner.result
data: {"run_id":"4a7e9c10-2b1a-4c3d-8e7f-6a5b4c3d2e1f","session_id":"1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c","canvas_id":"9e21d4f0-6c3b-4a2d-8f1e-0d9c8b7a6f5e","editor_url":"https://app.aidesigner.ai/editor/1b8f3c2a-3d4e-4f5a-9b1c-8e7d6f5a4b3c","canvas_delivery":"streamed"}
data: [DONE]It's a named event, so parsers that only read default data: lines never see it — existing streaming integrations keep working unchanged even after you opt in.
canvas_delivery values
| Value | Meaning |
|---|---|
| streamed | Canvas HTML was written live, chunk by chunk, as the streaming response generated. |
| appended | Canvas HTML was written once, on completion. Always the case for non-streaming requests, and also used for streaming requests when live delivery is temporarily disabled server-side. |
| skipped | The canvas write failed. Generation still completed, you are still billed, and the HTML is still returned in the response — only the automatic canvas placement did not happen. |
Errors specific to session delivery
| Status | Meaning |
|---|---|
| 400 | canvas_id was sent without session, or session / canvas_id was not a valid UUID (or "new"). |
| 404 | The session or canvas doesn't exist, or doesn't belong to your account. |
| 409 | canvas_busy — a concurrent request is already refining that canvas. Retry once it finishes. |
| 429 | The Sessions endpoints share a separate rate-limit bucket from generation: 60 requests / 60s per account. |
Ultradesign
Pass design_mode: "ultradesign" on /generateDesign to opt into AIDesigner's premium generation workflow. On a fresh generation, a planner LLM compiles a hidden reference-image prompt, generates a high-quality reference image from it, and the design model works from that visual direction instead of the prompt alone. The reference image is never returned to you — it only steers the HTML that comes back.
Fresh generation vs. edit
The request is treated as an ultradesign edit — an edit-tuned system prompt, no hidden reference image, no extra credits — when either is true: canvas_id is set, or messages contains any turn with role: "assistant". An assistant turn implies a prior design already exists in the conversation, so callers replaying conversation history with a previous design get edit behavior automatically — you don't have to pass canvas_id for it to take effect.
On a fresh generation, the hidden reference image is steered by the latest user turn with non-empty text (its text parts joined, for content-array messages). Ultradesign works best with a rich, specific latest user message — vague prompts produce a vague reference image.
Credits
The design generation itself bills exactly like classic mode — 1 credit plus any overage. Ultradesign adds 1–2 extra credits for the hidden reference image on a fresh generation only (edits skip it entirely). Those image credits are billed by the image-generation step itself and are charged even if the design step that follows fails — they are not refunded. When ultradesign is attached to a session, it additionally extracts up to 7 reference assets from the hidden image into the session, billed separately by the extraction step. Non-text assets bill roughly 1 credit per 3; text-bearing assets (logos, buttons with copy, etc.) bill 1 credit each, so a session-attached run can cost up to ~7 extra credits in the worst case. Sessionless ultradesign requests skip asset extraction entirely — the reference image still steers generation, there's just no session to extract into.
design_mode in the response
design_mode is echoed back in the non-streaming JSON body whenever a session is attached or ultradesign is used. A classic, sessionless request's response is unchanged — the field is absent, exactly as before this parameter existed. It is also omitted on idempotent-replay responses. The aidesigner.result SSE event only exists on session-attached streams — see Canvas Delivery above. A sessionless streaming ultradesign request gets no named event at all, only content deltas and data: [DONE]. When the event is emitted, it carries design_mode.
curl -X POST https://api.aidesigner.ai/api/v1/generateDesign \
-H "Authorization: Bearer $AIDESIGNER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A premium fintech dashboard with account balances, spend charts, and a recent-activity feed",
"design_mode": "ultradesign",
"viewport": "desktop",
"streaming": false
}'Website Analysis Modes
When you include both mode and url, AIDesigner scrapes the target page before generation.
Borrow the brand feel while allowing more creative freedom in layout and structure.
Aim for a close recreation of the reference layout, spacing, and visual treatment.
Preserve the original content while improving presentation and overall polish.
Response & Credits
Streaming is the default. Use server-sent events when you want incremental HTML output, or set streaming: false for one JSON response. Each request costs at least one credit. Website analysis modes charge an extra credit for the scrape and analysis step.
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"<!DOCTYPE html>..."},"finish_reason":null}]}
data: [DONE]{
"success": true,
"content": "<!DOCTYPE html>...",
"usage": {
"prompt_tokens": 1234,
"completion_tokens": 5678,
"total_tokens": 6912
}
}Error Codes
Standard HTTP status codes are used. Authentication, malformed payloads, and insufficient-credit failures happen before generation begins.
| Status | Meaning |
|---|---|
| 400 | Invalid JSON, missing prompt/messages, or missing URL when website analysis is enabled. |
| 401 | Missing, malformed, revoked, or invalid API key. |
| 403 | The account does not have enough credits for the request. |
| 429 | The account exceeded the request window or already has too many in-flight generations. Check Retry-After and the X-RateLimit-* headers before retrying. |
| 500 / 502 | Internal generation or upstream model failure. |