Jobs
Durable runs: hand the agent team a goal, walk away, and collect the posts, carousels and video it made.
A post you already wrote goes out through Posts. A job is the other direction: you hand over a goal and the agent team researches, writes, designs and renders, then leaves the result in the queue for approval.
A job is durable. You send a goal, get a 202 with its id, and the run keeps going whether or not
you stay connected - which is the only shape that survives a terminal closing, a lambda timing out
or a cron window ending.
Start one
Needs write. label becomes the title; without one the goal's first line is. budgetUsd caps
what the run may spend. timezone tells it what "tomorrow morning" means.
Watch it, or come back
| Method | Path | Scope | Purpose |
|---|---|---|---|
| GET | /api/v1/jobs | read | The list, 200 per page, newest first, with a nextCursor. |
| GET | /api/v1/jobs/[id] | read | Transcript, status, live, and an outputs summary. |
| POST | /api/v1/jobs/[id]/messages | write | Another turn on the same job. |
| GET | /api/studio/tail?conversationId=[id] | read | The live event stream. |
| POST | /api/studio/stop | write | Stop it. |
Follow the run rather than polling it: shapeless jobs tail <id>, or the jobs_tail MCP tool,
which holds one bounded call (60 seconds or 200 events) and answers a cursor to resume from. A
job with no live stream answers {live: false} instead of erroring, and a finished one replays
and closes.
jobs_brief is the cheap read before replying: the last 30 messages clipped, reasoning and
tool-activity dropped, an artifact inventory, and post counts per queue status. Deterministic, no
model in the loop. jobs_get still gives the whole transcript.
Resume a stuck run
Posting another message rebuilds the conversation server-side from the stored transcript, so "continue" is a real resume, not a fresh start:
Attach files
attachments puts files on the message itself, and the agent sees them: an image's pixels are
inlined for that turn, video and PDF go over by storage URI, and the media key is named in the text
so it survives in history after the pixels are gone.
Up to 6 per message. Each entry needs a name and either text (inlined, 24k characters) or a
mediaKey. Get a key by uploading to PUT /api/studio/attachments/[filename] (10MB images, 30MB
video and PDF; filenames are [A-Za-z0-9._-]), or name any media key the account already owns - a
brand asset, something an earlier run made. A key belonging to another account is a 400 naming
the file, not a run that dies halfway. A malformed entry is a 400 too: attachments are never
dropped silently.
The CLI and MCP take local paths directly:
Cast an actor
An actor is workspace-level identity - a face, a voice, a style, a memory - that posts and jobs
cast. characters on a job is that cast, by slug or id:
The agent is shown the whole roster either way, so it reuses an actor that fits instead of
inventing another one. A ref that names nobody is a 400 naming it, never a run that silently
stars nobody.
Handing work back to the human
Every job result carries url - https://shapelessai.com/studio/c/<id> - the conversation the
human opens to see what happened and approve what came out. Hand it over rather than pasting a
transcript.
Money
A job spends credits: research, writing, images, video, voice. Free accounts carry $5 a month, refilled monthly.
budgetUsd caps one run. An empty wallet is a 402 with code: "trial_exhausted", never a run
that quietly does less. GET /api/me carries the balance. Composing, scheduling and
publishing through the rail spend nothing - only the agent team does.
