imageat Workflow API
Run published workflows from your own application. Generate images, video, and multi-step AI pipelines via a simple REST API.
Introduction
The Workflow API lets you trigger a published imageat workflow with a single HTTP request, poll for completion, and receive CDN-hosted outputs. Every run is metered and billed against the API key's owning account.
A typical integration is three calls: POST to create a run, GET (or subscribe via SSE) to track progress, and then read CDN URLs from the final response.
Authentication
All requests are authenticated with an API key sent as a Bearer token. Create keys from your Projects page. Keys start with iat_live_ (production) or iat_test_ (sandbox).
Authorization: Bearer iat_live_xxxxxxxxxxxxxxxx
Keep your API key on the server. Never expose it in client-side code or commit it to source control. Revoke and rotate from the Projects page if a key is exposed.
Run lifecycle
A run moves through these states:
queued— accepted, waiting for a workerrunning— worker executing nodescompleted— all outputs readyfailed— one or more nodes failed; checkerrorcancelled— cancelled before completion
Credits are reserved at run creation and settled (refund of unused portion) once the run completes or fails. The final response includes credits_used.
Create a run
POST /v1/workflows/{workflow_id}/runs — start a run of a published workflow.
POST https://api.imageat.com/v1/workflows/wf_abc123/runs
Authorization: Bearer iat_live_...
Content-Type: application/json
Idempotency-Key: 8f3b... (optional)
{
"nodes": [],
"edges": [],
"webhook": {
"url": "https://your-app.com/webhook/imageat",
"secret": "whsec_..."
}
}If you pass empty nodes/edges, the saved (published) workflow graph is used. Pass them to override inputs (e.g. a different prompt) per-run.
{
"run_id": "run_01HX...",
"status": "queued"
}Idempotency: send the same Idempotency-Key to safely retry a request — the same run is returned instead of creating a duplicate.
Poll a run
GET /v1/runs/{run_id}
{
"run_id": "run_01HX...",
"status": "completed",
"outputs": {
"node_output_1": {
"type": "image",
"value": "https://cdn.imageat.com/runs/run_01HX.../node_output_1.png"
}
},
"credits_used": 12,
"created_at": "2026-05-22T10:01:24Z",
"completed_at": "2026-05-22T10:01:38Z"
}Poll every 1–2 seconds while the status is queued or running. For faster feedback, use SSE.
Server-sent events (live updates)
GET /v1/runs/{run_id}/events
Returns a text/event-stream. Each event is a JSON line emitted as nodes start, finish, or the run terminates. The stream closes after run_complete or run_failed.
data: {"event":"node_start","node_id":"n1"}
data: {"event":"node_complete","node_id":"n1","output":{...}}
data: {"event":"run_complete","run_id":"run_01HX..."}Cancel a run
POST /v1/runs/{run_id}/cancel
Cancels a queued or running run. Reserved credits are refunded; already-executed node costs are kept.
{ "ok": true }Outputs & CDN URLs
Final outputs are mirrored to cdn.imageat.com so URLs are stable and fast. The path format is:
https://cdn.imageat.com/runs/{run_id}/{node_id}.{ext}Each output entry has a type describing how to consume the value:
| type | value |
|---|---|
| image | Single CDN image URL |
| images | Array of image URLs |
| video | CDN video URL (mp4) |
| text | Plain text (e.g. LLM output) |
Webhooks
Pass a webhook object on run creation to receive a signed POST when the run terminates — no polling required.
POST https://your-app.com/webhook/imageat
X-Imageat-Signature: t=1716372924,v1=sha256(...)
Content-Type: application/json
{
"event": "run_complete",
"run_id": "run_01HX...",
"status": "completed",
"outputs": { ... },
"credits_used": 12
}Verify X-Imageat-Signature using your webhook secret: compute HMAC-SHA256 over {timestamp}.{raw_body} and compare in constant time.
Error codes
| status | meaning |
|---|---|
| 401 | Missing or invalid API key |
| 402 | Insufficient credits — response includes balance and required |
| 403 | Run belongs to a different account |
| 404 | Workflow or run not found, or workflow not published |
| 422 | Invalid request body (bad nodes/edges) |
| 500 | Internal worker error — safe to retry with the same Idempotency-Key |
Code examples
A full create → poll → read flow in three languages.
# 1. Create a run
RUN=$(curl -s -X POST https://api.imageat.com/v1/workflows/wf_abc123/runs \
-H "Authorization: Bearer $IMAGEAT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"nodes": [], "edges": []}' | jq -r .run_id)
# 2. Poll until complete
while true; do
RES=$(curl -s https://api.imageat.com/v1/runs/$RUN \
-H "Authorization: Bearer $IMAGEAT_API_KEY")
STATUS=$(echo "$RES" | jq -r .status)
[ "$STATUS" = "completed" ] && echo "$RES" | jq .outputs && break
[ "$STATUS" = "failed" ] && echo "$RES" | jq . && exit 1
sleep 2
doneconst API_KEY = process.env.IMAGEAT_API_KEY!;
const BASE = "https://api.imageat.com/v1";
async function runWorkflow(workflowId: string) {
const create = await fetch(`${BASE}/workflows/${workflowId}/runs`, {
method: "POST",
headers: {
Authorization: `Bearer ${API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ nodes: [], edges: [] }),
});
if (!create.ok) throw new Error(`create failed: ${create.status}`);
const { run_id } = await create.json();
while (true) {
await new Promise((r) => setTimeout(r, 1500));
const poll = await fetch(`${BASE}/runs/${run_id}`, {
headers: { Authorization: `Bearer ${API_KEY}` },
});
const data = await poll.json();
if (data.status === "completed") return data;
if (data.status === "failed") throw new Error(data.error);
}
}
const result = await runWorkflow("wf_abc123");
console.log(result.outputs, result.credits_used);import os, time, requests
API_KEY = os.environ["IMAGEAT_API_KEY"]
BASE = "https://api.imageat.com/v1"
H = {"Authorization": f"Bearer {API_KEY}"}
def run_workflow(workflow_id: str) -> dict:
r = requests.post(
f"{BASE}/workflows/{workflow_id}/runs",
headers={**H, "Content-Type": "application/json"},
json={"nodes": [], "edges": []},
)
r.raise_for_status()
run_id = r.json()["run_id"]
while True:
time.sleep(1.5)
data = requests.get(f"{BASE}/runs/{run_id}", headers=H).json()
if data["status"] == "completed":
return data
if data["status"] == "failed":
raise RuntimeError(data.get("error", "run failed"))
result = run_workflow("wf_abc123")
print(result["outputs"], result["credits_used"])imageat MCP Server
Use imageat directly from AI clients like Claude Desktop, Cursor, and any other tool that speaks the Model Context Protocol — no code required.
Overview
The @imageat/mcp package is a Model Context Protocol (MCP) server. Once connected, your AI client gains tools to generate images, generate video, and run every imageat edit feature — all billed against your account credits. Edit tools are discovered live, so new features appear automatically.
Published on npm: npmjs.com/package/@imageat/mcp
Install
You only need Node.js 18+ and an API key from your Projects page. Add the server to your client's MCP config:
{
"mcpServers": {
"imageat": {
"command": "npx",
"args": ["-y", "@imageat/mcp"],
"env": {
"IMAGEAT_API_KEY": "iat_live_xxxxxxxxxxxx"
}
}
}
}On macOS this file lives at ~/Library/Application Support/Claude/claude_desktop_config.json. Restart the client after editing. The imageat tools will appear in the tools menu.
| env var | notes |
|---|---|
| IMAGEAT_API_KEY | Required. Your iat_live_ key. |
| IMAGEAT_BASE_URL | Optional. Defaults to https://api.imageat.com. |
Tools
The server exposes these tools to the connected client:
| tool | what it does |
|---|---|
| imageat_generate_image | Text-to-image / image-to-image. Returns CDN image URL(s). |
| imageat_generate_video | Text-to-video / image-to-video. Returns a CDN mp4 URL. |
| imageat_check_credits | Current credit balance. |
| imageat_edit_* | One tool per edit feature — remove background, object eraser, relight, virtual try-on, city teleport, inpaint, and more. Fetched live, so new features appear automatically. |
Same auth and billing as the REST API — every call is metered against the API key's account, and errors return clear messages (e.g. 402 with your balance when out of credits).