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:
- You POST a message to
/sessions/{id}/chator trigger a skill via/sessions/{id}/run-skill. - The server enqueues a background job and returns a
job_idimmediately. - A background worker (SAQ) executes the agent turn — planning, calling tools, writing events.
- You poll
GET /jobs/{job_id}/events?since=<cursor>every 1–2 seconds untildone: 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: truerun 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"
}
]
| Field | Type | Description |
|---|---|---|
id | string | Unique session identifier |
name | string | Human-readable session name |
credits_spent | number | Total credits consumed in this session |
asset_count | number | Total number of generated/uploaded assets |
created_at | ISO 8601 | Session creation timestamp |
updated_at | ISO 8601 | Last 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
| Param | Type | Description |
|---|---|---|
session_id | string | ID 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"
}
]
| Field | Type | Description |
|---|---|---|
asset_label | string | Agent-addressable label (e.g. asset_1). Used in chat to reference specific assets. |
kind | string | "image" | "video" | "audio" |
url | string | CDN URL of the media file |
source_tool | string | How the asset was created: "upload", "generate_image", "edit_image", "image_to_video", etc. |
model | string | null | Model used to generate the asset |
prompt | string | null | Prompt used when generating |
created_at | ISO 8601 | Asset 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"
}
| Field | Type | Required | Description |
|---|---|---|---|
url | string | ✅ | Public CDN URL of the uploaded file |
kind | string | ✅ | "image" | "video" | "audio" |
source_tool | string | ❌ | Defaults to "upload" |
prompt | string | ❌ | Optional 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_labelis 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 }
]
}
}
| Field | Type | Required | Description |
|---|---|---|---|
message | string | ✅ | The user's message to the agent |
model | string | ❌ | Planner LLM to use. Defaults to gpt-5-mini. |
messages_snapshot | array | ❌ | Full chat history prior to this turn (for context). Persisted atomically. |
canvas_state | object | ❌ | Snapshot 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}/eventsto 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": [...]
}
| Field | Type | Required | Description |
|---|---|---|---|
skill_name | string | ✅ | Exact slug name of the skill (see GET /agent-skills) |
inputs | object | ❌ | Key-value map of skill input variables |
model | string | ❌ | Planner LLM. Defaults to gpt-5-mini. |
messages_snapshot | array | ❌ | Chat 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 Name | Description | Key Models |
|---|---|---|
selfie-with-celebrities | Compose selfie with movie actor | nano-banana-2-edit, kling-o1-image-to-video |
animal-video-generator | Funny anthropomorphic animal vlogger | nano-banana, veo3.1-fast-image-to-video |
ugc-ads-workflow | Influencer-style product UGC ad | gpt-image-2, seedance-v1.5-pro-i2v-fast |
cartoon-dance-animation | Pixar-style 3D character dance | nano-banana-2-edit, kling-v2.6-std-motion-control |
character-story-video | Multi-part story video from images | nano-banana-2-edit |
action-figure-generator | 3D collectible action figure in packaging | nano-banana-2-edit |
fashion-try-on | Virtual clothing try-on + model video | qwen-image-edit-2511, seedance-v1.5-pro-i2v-fast |
floor-plan-rendering | 2D floor plan → 3D architectural rendering | nano-banana-2, nano-banana-2-edit |
interior-design-visualizer | Empty room → furnished interior | gpt-image-2, nano-banana-2-edit |
multi-angle-reshoot | 6 dramatic camera angle variations | nano-banana-2-edit |
couple-grid-creator | 6-box romantic couple pose grid | qwen-image-edit-plus |
giant-product-showcase | Person next to building-sized product | nano-banana-2-edit, veo3.1-fast-image-to-video |
product-showcase-video | Explosive ingredient product ad | bytedance-seedream-v5.0-edit, seedance-v1.5-pro-i2v-fast |
jewelry-product-video | Luxury macro jewelry commercial | nano-banana-2-edit, grok-imagine-image-to-video |
3d-logo-animation | 2D logo → 3D animated reveal | nano-banana-2-edit, veo3.1-fast-image-to-video |
talking-baby-video | Viral talking baby in costume | nano-banana, grok-imagine-image-to-video |
keyboard-art-maker | Custom message spelled in keycaps | ideogram-v3-t2i |
product-video-ad-maker | Cinematic product video ad | flux-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
| Param | Type | Default | Description |
|---|---|---|---|
since | integer | 0 | Return only events with id > since. Start at 0 for a fresh poll. |
limit | integer | 200 | Max 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
}
| Field | Type | Description |
|---|---|---|
status | string | Current state: pending, processing, completed, failed |
approved | boolean | null | true if approved, false if rejected, null if pending |
approval_requested | boolean | true if the agent is currently waiting for user confirmation |
cursor | number | Cursor for the next poll |
events | object[] | List of events since the last poll |
error | string | Error message (if status is failed) |
done | boolean | true if job has finished |
Event Types Reference
type | Description | Key Payload Fields |
|---|---|---|
text | Streaming agent text (chat bubble content) | content: string |
info | Status message (e.g. "Waiting for approval...") | content: string, needs_approval: boolean |
error | An error occurred | message: string |
tool_call | Agent is about to call a tool | name: string, args: object |
tool_result | A tool call completed. Contains the new asset. | name: string, result: object, asset: AssetObject |
plan_propose | Agent proposed a generation plan pending approval | title: string, nodes: array, total_credits: number |
canvas_op | Canvas spatial operation (move/arrange) | op: string, args: object |
Data Models
AssetObject
Represents a media file produced or used by the agent.
| Field | Type | Description |
|---|---|---|
asset_label | string | Unique label in the session (e.g. asset_1) |
kind | string | "image" | "video" | "audio" |
url | string | Public CDN URL |
source_tool | string | Tool that created the asset |
model | string | null | AI model used |
prompt | string | null | Prompt used |
created_at | string | ISO 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"
}
| Status | Meaning |
|---|---|
400 | Bad request / validation error |
401 | Missing or invalid auth token |
403 | Forbidden (accessing another user's resource) |
404 | Session, job, asset, or skill not found |
422 | Invalid field value (e.g., unsupported kind) |
500 | Internal server error |
[!WARNING] If
done: trueandstatus: "failed"in the poll response, check theerrorfield for a sanitized error message from the model provider.
Rate Limits & Credits
- Each tool call consumes credits from your account balance.
- The
plan_proposeevent shows atotal_creditsestimate before execution. - Use
POST /jobs/{job_id}/rejectto cancel without spending credits. - Sandbox API keys (
is_test: true) do not consume credits.