API Reference

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
Read MCP docs

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 Header
Authorization: Bearer gp_your_secret_api_key

Never 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.

LimitDefaultScope
Generation requests30 / 60sShared across all API keys on the same account
Concurrent generations4 in flightShared 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.

Rate-Limit Headers
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

POST/api/v1/generateDesign

Generates 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

POST/api/v1/sessions
title
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.

201 Response
{
  "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

GET/api/v1/sessions
limit
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.

200 Response
{
  "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

GET/api/v1/sessions/:id
id
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.

200 Response
{
  "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

GET/api/v1/sessions/:id/canvases/:canvasId
id
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

POST/api/v1/sessions/:id/publish

Publishes 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.

Request
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 '{}'
200 Response
{
  "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

GET/api/v1/sessions/:id/publish
id
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.

200 Response
{
  "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

DELETE/api/v1/sessions/:id/publish
id
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.

200 Response
{
  "published": false,
  "released_subdomain": "coffee-landing-4f2a",
  "removed_custom_domain": null
}

Claim a custom subdomain

PATCH/api/v1/sessions/:id/publish/subdomain

Requires 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.

200 Response
{
  "subdomain": "my-coffee-co",
  "published_url": "https://my-coffee-co.aidesigner.site"
}

Check subdomain availability

GET/api/v1/subdomains/check
subdomain
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.

Request
curl -G https://api.aidesigner.ai/api/v1/subdomains/check \
  -H "Authorization: Bearer $AIDESIGNER_API_KEY" \
  --data-urlencode "subdomain=my-coffee-co"
200 Response
{
  "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.

Statuserror codeMeaning
404session_not_foundThe session doesn't exist or doesn't belong to your account.
422nothing_to_publishThe session has no canvases yet — generate at least one before publishing.
422content_blockedModeration 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.
409not_publishedA subdomain operation was attempted on a session that has never been published.
409subdomain_takenAnother site already holds that subdomain. Check availability first if you want to avoid this.
400invalid_subdomainThe requested subdomain fails validation (length, characters, hyphen rules) or is a reserved name like www or admin.
500publish_failedAn 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

PUT/api/v1/sessions/:id/domain

Registers 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.

Request
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" }'
200 Response
{
  "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

GET/api/v1/sessions/:id/domain
200 Response
{ "configured": false }

// or, once a domain is connected:
{
  "configured": true,
  "domain": "example.com",
  "status": "pending",
  "ssl_status": "pending_validation"
}

Re-check DNS

POST/api/v1/sessions/:id/domain/verify

Re-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.

200 Response
{
  "domain": "example.com",
  "dns_verified": true,
  "ssl_status": "active",
  "status": "active",
  "error": null
}

Disconnect a domain

DELETE/api/v1/sessions/:id/domain

Removes 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

GET/api/v1/domains/setup

Independent 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).

Request
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"
200 Response
{
  "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.

Statuserror codeMeaning
400invalid_domainThe 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.
402subscription_requiredCustom domains require an active PRO subscription on the session's team.
404session_not_foundThe session doesn't exist, or your API key's account isn't its owner.
409not_publishedThe session must be published before a domain can be attached.
409domain_takenAnother site already has this domain connected.
502ssl_provider_errorRegistering the domain with the SSL/hosting provider failed. Retry — this is usually transient.
500domain_failedAn 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:

Statuserror codeMeaning
504detection_timeoutProvider 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:

Session Headers
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-6a5b4c3d2e1f

X-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.

SSE Event
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

ValueMeaning
streamedCanvas HTML was written live, chunk by chunk, as the streaming response generated.
appendedCanvas 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.
skippedThe 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

StatusMeaning
400canvas_id was sent without session, or session / canvas_id was not a valid UUID (or "new").
404The session or canvas doesn't exist, or doesn't belong to your account.
409canvas_busy — a concurrent request is already refining that canvas. Retry once it finishes.
429The 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.

Request
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.

inspire

Borrow the brand feel while allowing more creative freedom in layout and structure.

clone

Aim for a close recreation of the reference layout, spacing, and visual treatment.

enhance

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.

Streaming Response
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]
JSON Response
{
  "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.

StatusMeaning
400Invalid JSON, missing prompt/messages, or missing URL when website analysis is enabled.
401Missing, malformed, revoked, or invalid API key.
403The account does not have enough credits for the request.
429The account exceeded the request window or already has too many in-flight generations. Check Retry-After and the X-RateLimit-* headers before retrying.
500 / 502Internal generation or upstream model failure.
AIDesigner REST API Reference | AIDesigner