Design Agent API

MUAPI Design Agent — API Reference

The Design Agent is a conversational AI system that can generate images, videos, and audio by orchestrating calls to generative AI models on the MUAPI platform. This document covers all API endpoints used by the web interface, intended for developers who want to integrate with or build on top of the agent programmatically.


Overview

Base URL: https://api.muapi.ai/api/v1/creative-agent
Auth:     x-api-key: {MUAPIAPP_API_KEY}
Format:   application/json

Architecture

The agent uses a submit-and-poll pattern:

  1. You POST a message to /sessions/{id}/chat or trigger a skill via /sessions/{id}/run-skill.
  2. The server enqueues a background job and returns a job_id immediately.
  3. A background worker (SAQ) executes the agent turn — planning, calling tools, writing events.
  4. You poll GET /jobs/{job_id}/events?since=<cursor> every 1–2 seconds until done: true.
sequenceDiagram
    participant "Client"
    participant "API"
    participant "Worker"
    participant "Model (MUAPI)"

    "Client"->>"API": POST /sessions/{id}/chat
    "API"-->>"Client": { job_id, status: "pending" }
    "API"->>"Worker": Enqueue task_run_agent_turn

    loop Polling
        "Client"->>"API": GET /jobs/{job_id}/events?since=0
        "API"-->>"Client": { events: [...], done: false }
    end

    "Worker"->>"Model (MUAPI)": generate_image / image_to_video / ...
    "Model (MUAPI)"-->>"Worker": result URL
    "Worker"->>"API": Write tool_result event

    "Client"->>"API": GET /jobs/{job_id}/events?since=N
    "API"-->>"Client": { events: [tool_result], done: true }

Authentication

All endpoints require an API key in the headers:

x-api-key: {MUAPIAPP_API_KEY}

The token can be a:

  • API Key (from the Muapi Dashboard)
  • User JWT (only used by the Muapi web client)

[!IMPORTANT] API keys with is_test: true run the agent end-to-end without billing.


Sessions

Sessions are persistent workspaces. Each session holds a chat history, generated assets, and a list of agent jobs.


GET /sessions

List all sessions belonging to the authenticated user, ordered by most recently updated.

Response 200 OK

[
  {
    "id": "sess_abc123",
    "name": "My Fashion Shoot",
    "credits_spent": 120,
    "asset_count": 5,
    "created_at": "2026-05-08T07:00:00Z",
    "updated_at": "2026-05-08T09:30:00Z"
  }
]
FieldTypeDescription
idstringUnique session identifier
namestringHuman-readable session name
credits_spentnumberTotal credits consumed in this session
asset_countnumberTotal number of generated/uploaded assets
created_atISO 8601Session creation timestamp
updated_atISO 8601Last activity timestamp

POST /sessions

Create a new session.

Request Body (optional)

{
  "name": "Product Campaign"
}

If name is omitted, a random name like session-a1b2c3 is generated.

Response 200 OK

{
  "id": "sess_xyz789",
  "name": "Product Campaign",
  "credits_spent": 0,
  "asset_count": 0
}

PATCH /sessions/{session_id}

Rename an existing session.

Path Parameters

ParamTypeDescription
session_idstringID of the session to rename

Request Body

{
  "name": "Summer Collection Ads"
}

Response 200 OK

{
  "id": "sess_xyz789",
  "name": "Summer Collection Ads"
}

DELETE /sessions/{session_id}

Permanently delete a session and all its associated assets, messages, and jobs.

Response 200 OK

{
  "status": "deleted"
}

Assets

Assets are any media file (image, video, or audio) attached to a session — either uploaded by the user or generated by the agent.


GET /sessions/{session_id}/assets

Retrieve all assets registered for a session, sorted by creation time (oldest first).

Response 200 OK

[
  {
    "asset_label": "asset_1",
    "kind": "image",
    "url": "https://cdn.muapi.ai/sessions/sess_xyz/asset_1.webp",
    "source_tool": "upload",
    "model": null,
    "prompt": null,
    "created_at": "2026-05-08T07:05:00Z"
  },
  {
    "asset_label": "asset_2",
    "kind": "image",
    "url": "https://cdn.muapi.ai/sessions/sess_xyz/asset_2.webp",
    "source_tool": "generate_image",
    "model": "nano-banana-2",
    "prompt": "A cinematic portrait of a woman in a black suit",
    "created_at": "2026-05-08T07:12:00Z"
  }
]
FieldTypeDescription
asset_labelstringAgent-addressable label (e.g. asset_1). Used in chat to reference specific assets.
kindstring"image" | "video" | "audio"
urlstringCDN URL of the media file
source_toolstringHow the asset was created: "upload", "generate_image", "edit_image", "image_to_video", etc.
modelstring | nullModel used to generate the asset
promptstring | nullPrompt used when generating
created_atISO 8601Asset registration timestamp

POST /sessions/{session_id}/assets

Register an externally-uploaded asset with a session so the agent can address it by label (e.g., asset_3) in subsequent turns.

[!TIP] This should be called after uploading the file directly to S3 using the signed URL from GET /api/app/get_file_upload_url. See the File Upload section below.

Request Body

{
  "url": "https://cdn.muapi.ai/uploads/my-photo.jpg",
  "kind": "image",
  "source_tool": "upload",
  "prompt": "User selfie for celebrity collage"
}
FieldTypeRequiredDescription
urlstringPublic CDN URL of the uploaded file
kindstring"image" | "video" | "audio"
source_toolstringDefaults to "upload"
promptstringOptional description/caption

Response 200 OK

{
  "asset_label": "asset_3",
  "kind": "image",
  "url": "https://cdn.muapi.ai/uploads/my-photo.jpg",
  "source_tool": "upload"
}

[!NOTE] The asset_label is auto-incremented atomically per session (e.g., asset_1, asset_2, etc.). Always use the returned label when referencing this asset in chat.


Chat Messages


GET /sessions/{session_id}/messages

Retrieve the full persisted chat history for a session.

Response 200 OK

[
  {
    "role": "user",
    "content": "Generate a 3D action figure of me in a doctor outfit",
    "attachments": [
      { "asset_label": "asset_1", "url": "...", "kind": "image" }
    ],
    "timestamp": "2026-05-08T07:10:00Z",
    "skill_name": "action-figure-generator"
  },
  {
    "role": "assistant",
    "content": "I'll create your action figure right away!",
    "events": [
      { "type": "tool_call", "name": "edit_image", "args": { ... } },
      { "type": "tool_result", "name": "edit_image", "result": { ... }, "asset": { ... } }
    ],
    "timestamp": "2026-05-08T07:10:05Z"
  }
]

PATCH /sessions/{session_id}/messages

Persist/overwrite the full chat history for a session. Called automatically by the frontend after each completed turn to ensure durability.

Request Body

{
  "messages": [
    { "role": "user", "content": "...", "timestamp": "..." },
    { "role": "assistant", "content": "...", "events": [...], "timestamp": "..." }
  ]
}

Response 200 OK

{
  "status": "saved",
  "count": 4
}

Agent Execution


POST /sessions/{session_id}/chat

Send a free-form message to the agent. The agent analyzes intent, plans tool calls, and executes them (generating images, videos, audio, etc.). This is the primary entry point for conversational interaction.

Request Body

{
  "message": "Create a selfie of me with Harry Potter on set",
  "model": "gpt-5-mini",
  "messages_snapshot": [...],
  "canvas_state": {
    "viewport": { "x": 0, "y": 0, "scale": 1 },
    "selected": "asset_2",
    "nodes": [
      { "asset_id": "asset_1", "kind": "image", "x": 100, "y": 200, "w": 400, "h": 400 },
      { "asset_id": "asset_2", "kind": "image", "x": 600, "y": 200, "w": 400, "h": 400 }
    ]
  }
}
FieldTypeRequiredDescription
messagestringThe user's message to the agent
modelstringPlanner LLM to use. Defaults to gpt-5-mini.
messages_snapshotarrayFull chat history prior to this turn (for context). Persisted atomically.
canvas_stateobjectSnapshot of the user's canvas layout — used for spatial reasoning ("use the asset on the left").

Response 200 OK

{
  "job_id": "job_def456",
  "status": "pending"
}

[!IMPORTANT] This endpoint returns immediately. The agent runs asynchronously. Poll GET /jobs/{job_id}/events to track progress.


POST /sessions/{session_id}/run-skill

Directly invoke a named expert skill, bypassing the agent's intent-detection step. The specified skill's recipe is rendered into a structured prompt and sent to the agent.

Request Body

{
  "skill_name": "fashion-try-on",
  "inputs": {
    "person_image": "asset_1",
    "clothing_image": "asset_2"
  },
  "model": "gpt-5-mini",
  "messages_snapshot": [...]
}
FieldTypeRequiredDescription
skill_namestringExact slug name of the skill (see GET /agent-skills)
inputsobjectKey-value map of skill input variables
modelstringPlanner LLM. Defaults to gpt-5-mini.
messages_snapshotarrayChat history snapshot (for persistence)

Response 200 OK

{
  "job_id": "job_ghi789",
  "status": "pending"
}

GET /agent-skills

List all available expert skill recipes loaded from agent/skills/*.skill.md.

Response 200 OK

[
  {
    "name": "fashion-try-on",
    "description": "Virtually try on different outfits...",
    "inputs": ["person_image", "clothing_image"],
    "trigger_keywords": ["fashion try on", "virtual fitting room", ...],
    "estimated_credits": 150
  },
  {
    "name": "action-figure-generator",
    "description": "Convert a photo of a person into a custom 3D action figure...",
    ...
  }
]

Available Skills (as of May 2026)

Skill NameDescriptionKey Models
selfie-with-celebritiesCompose selfie with movie actornano-banana-2-edit, kling-o1-image-to-video
animal-video-generatorFunny anthropomorphic animal vloggernano-banana, veo3.1-fast-image-to-video
ugc-ads-workflowInfluencer-style product UGC adgpt-image-2, seedance-v1.5-pro-i2v-fast
cartoon-dance-animationPixar-style 3D character dancenano-banana-2-edit, kling-v2.6-std-motion-control
character-story-videoMulti-part story video from imagesnano-banana-2-edit
action-figure-generator3D collectible action figure in packagingnano-banana-2-edit
fashion-try-onVirtual clothing try-on + model videoqwen-image-edit-2511, seedance-v1.5-pro-i2v-fast
floor-plan-rendering2D floor plan → 3D architectural renderingnano-banana-2, nano-banana-2-edit
interior-design-visualizerEmpty room → furnished interiorgpt-image-2, nano-banana-2-edit
multi-angle-reshoot6 dramatic camera angle variationsnano-banana-2-edit
couple-grid-creator6-box romantic couple pose gridqwen-image-edit-plus
giant-product-showcasePerson next to building-sized productnano-banana-2-edit, veo3.1-fast-image-to-video
product-showcase-videoExplosive ingredient product adbytedance-seedream-v5.0-edit, seedance-v1.5-pro-i2v-fast
jewelry-product-videoLuxury macro jewelry commercialnano-banana-2-edit, grok-imagine-image-to-video
3d-logo-animation2D logo → 3D animated revealnano-banana-2-edit, veo3.1-fast-image-to-video
talking-baby-videoViral talking baby in costumenano-banana, grok-imagine-image-to-video
keyboard-art-makerCustom message spelled in keycapsideogram-v3-t2i
product-video-ad-makerCinematic product video adflux-2-pro-edit, wan2.5-image-to-video-fast

Job Management

Jobs represent a single agent turn (one message → one response cycle).


GET /jobs/{job_id}/events

Poll for streaming events produced by the agent during a job turn. Use cursor-based pagination to receive only new events since the last poll.

Query Parameters

ParamTypeDefaultDescription
sinceinteger0Return only events with id > since. Start at 0 for a fresh poll.
limitinteger200Max events to return per call (max 1000).

Response 200 OK

{
  "job_id": "job_def456",
  "status": "processing",
  "error": null,
  "approved": null,
  "approval_requested": false,
  "cursor": 42,
  "events": [
    {
      "id": 38,
      "type": "text",
      "payload": { "content": "I'll create your action figure now..." },
      "job_id": "job_def456",
      "created_at": "2026-05-08T07:10:06Z"
    },
    {
      "id": 39,
      "type": "plan_propose",
      "payload": {
        "title": "Action Figure Generation Plan",
        "nodes": [
          { "tool": "edit_image", "model": "nano-banana-2-edit", "credits": 80 }
        ],
        "total_credits": 80
      },
      "job_id": "job_def456",
      "created_at": "2026-05-08T07:10:07Z"
    },
    {
      "id": 42,
      "type": "tool_result",
      "payload": {
        "name": "edit_image",
        "result": {
          "url": "https://cdn.muapi.ai/...",
          "source_asset_id": "asset_1"
        },
        "asset": {
          "asset_label": "asset_2",
          "kind": "image",
          "url": "https://cdn.muapi.ai/sessions/sess_xyz/asset_2.webp"
        }
      },
      "job_id": "job_def456",
      "created_at": "2026-05-08T07:10:45Z"
    }
  ],
  "done": true
}
FieldTypeDescription
statusstringCurrent state: pending, processing, completed, failed
approvedboolean | nulltrue if approved, false if rejected, null if pending
approval_requestedbooleantrue if the agent is currently waiting for user confirmation
cursornumberCursor for the next poll
eventsobject[]List of events since the last poll
errorstringError message (if status is failed)
donebooleantrue if job has finished

Event Types Reference

typeDescriptionKey Payload Fields
textStreaming agent text (chat bubble content)content: string
infoStatus message (e.g. "Waiting for approval...")content: string, needs_approval: boolean
errorAn error occurredmessage: string
tool_callAgent is about to call a toolname: string, args: object
tool_resultA tool call completed. Contains the new asset.name: string, result: object, asset: AssetObject
plan_proposeAgent proposed a generation plan pending approvaltitle: string, nodes: array, total_credits: number
canvas_opCanvas spatial operation (move/arrange)op: string, args: object

Data Models

AssetObject

Represents a media file produced or used by the agent.

FieldTypeDescription
asset_labelstringUnique label in the session (e.g. asset_1)
kindstring"image" | "video" | "audio"
urlstringPublic CDN URL
source_toolstringTool that created the asset
modelstring | nullAI model used
promptstring | nullPrompt used
created_atstringISO 8601 timestamp

GET /sessions/{session_id}/jobs

List the 20 most recent jobs for a session. Useful for detecting and resuming in-flight jobs after a page refresh.

Response 200 OK

[
  {
    "id": "job_def456",
    "status": "processing",
    "user_message": "Create an action figure of me",
    "error": null,
    "created_at": "2026-05-08T07:10:00Z",
    "completed_at": null
  }
]

POST /jobs/{job_id}/approve

Approve a proposed plan. The agent is waiting for this before executing tool calls. Poll /jobs/{job_id}/events to confirm the agent has resumed.

Response 200 OK

{
  "status": "approved"
}

POST /jobs/{job_id}/reject

Reject a proposed plan. The agent will stop execution and report the rejection.

Response 200 OK

{
  "status": "rejected"
}

POST /jobs/{job_id}/cancel

Cancel an in-progress job. The worker checks this flag between tool calls and stops dispatching new ones. Already-in-flight model requests may still complete.

Response 200 OK

{
  "status": "cancelled"
}

File Upload

The Design Agent uses direct-to-S3 uploads to avoid passing binary data through the API server.

Step 1 — Get a Signed Upload URL

GET /api/app/get_file_upload_url (Dashboard API — separate prefix)

GET /api/app/get_file_upload_url?filename=my-photo.jpg
x-api-key: {MUAPIAPP_API_KEY}

Response

{
  "url": "https://s3.amazonaws.com/muapi-uploads/...",
  "fields": {
    "key": "uploads/user_123/abc/my-photo.jpg",
    "AWSAccessKeyId": "...",
    "policy": "...",
    "signature": "..."
  }
}

Step 2 — Upload Directly to S3

POST <url from above>
Content-Type: multipart/form-data

[all fields from response, then the file itself]

The final CDN URL is: https://cdn.muapi.ai/<fields.key>

Step 3 — Register with Session

POST /api/v1/creative-agent/sessions/{session_id}/assets
{
  "url": "https://cdn.muapi.ai/uploads/user_123/abc/my-photo.jpg",
  "kind": "image",
  "source_tool": "upload"
}

The response gives you the asset_label (e.g. "asset_3") to use in subsequent messages.


Complete Workflow Example

Below is a full end-to-end example using JavaScript (fetch):

const API = "https://api.muapi.ai/api/v1/creative-agent";
const headers = {
  "Content-Type": "application/json",
  "x-api-key": "YOUR_API_KEY",
};

// 1. Create a session
const session = await fetch(`${API}/sessions`, {
  method: "POST",
  headers,
  body: JSON.stringify({ name: "My Campaign" }),
}).then((r) => r.json());
// => { id: "sess_abc", name: "My Campaign", ... }

// 2. (Optional) Upload a reference image
const uploadRes = await fetch(
  `/api/app/get_file_upload_url?filename=product.jpg`,
  { headers },
).then((r) => r.json());
// ... upload file to S3 using uploadRes.url and uploadRes.fields ...
const assetRes = await fetch(`${API}/sessions/${session.id}/assets`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    url: `https://cdn.muapi.ai/${uploadRes.fields.key}`,
    kind: "image",
  }),
}).then((r) => r.json());
// => { asset_label: "asset_1", ... }

// 3. Send a message (referencing the uploaded asset)
const job = await fetch(`${API}/sessions/${session.id}/chat`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    message: `Create a viral product showcase video using asset_1`,
    model: "gpt-5-mini",
  }),
}).then((r) => r.json());
// => { job_id: "job_xyz", status: "pending" }

// 4. Poll for events
let cursor = 0;
let done = false;
while (!done) {
  await new Promise((r) => setTimeout(r, 1500));
  const poll = await fetch(`${API}/jobs/${job.job_id}/events?since=${cursor}`, {
    headers,
  }).then((r) => r.json());

  for (const ev of poll.events) {
    if (ev.type === "plan_propose") {
      // Approve the plan automatically (or show user for confirmation)
      await fetch(`${API}/jobs/${job.job_id}/approve`, {
        method: "POST",
        headers,
      });
    }
    if (ev.type === "tool_result" && ev.payload.asset) {
      console.log("Generated asset:", ev.payload.asset.url);
    }
    if (ev.type === "text") {
      process.stdout.write(ev.payload.content);
    }
  }

  cursor = poll.cursor;
  done = poll.done;
}

// 5. Fetch final asset list
const assets = await fetch(`${API}/sessions/${session.id}/assets`, {
  headers,
}).then((r) => r.json());
console.log("All session assets:", assets);

Error Handling

All errors return standard HTTP status codes with a JSON body:

{
  "detail": "Session not found"
}
StatusMeaning
400Bad request / validation error
401Missing or invalid auth token
403Forbidden (accessing another user's resource)
404Session, job, asset, or skill not found
422Invalid field value (e.g., unsupported kind)
500Internal server error

[!WARNING] If done: true and status: "failed" in the poll response, check the error field for a sanitized error message from the model provider.


Rate Limits & Credits

  • Each tool call consumes credits from your account balance.
  • The plan_propose event shows a total_credits estimate before execution.
  • Use POST /jobs/{job_id}/reject to cancel without spending credits.
  • Sandbox API keys (is_test: true) do not consume credits.