For agents
The caller of this API is a program, and increasingly the program is an agent: something that reads a catalogue, decides, calls, and has to know afterwards whether it worked and what it cost. This page is the map for that reader: which surface to use, the rules to follow, and the URLs that answer without a key.
Which door
Every surface reaches the same API with the same key. The question is what the agent has.
| the agent has | use | why |
|---|---|---|
| an MCP client and no shell — Claude Desktop, a hosted agent, a chat product | the MCP server | nothing to install on the agent's side; hosted or local. Tool definitions are present on every turn |
| a shell — Claude Code, Codex, Cursor, an agent inside a repository or a terminal | the agent skill + the CLI | local files and whole directories, batches, and a script the agent leaves behind that a person can read and run again. The skill loads only when a task needs it |
| its own runtime — a workflow engine, a backend, a cron job | an SDK, REST or the n8n node | the same ops, jobs and presets as typed clients; webhooks instead of polling |
| both MCP and the skill configured | the skill says which | MCP when no local files or script are needed; the CLI otherwise |
Where the doors differ in what they can reach — the rows that most often decide it:
| Capability | MCP server | CLI + skill | SDKs · REST | n8n node |
|---|---|---|---|---|
| local files | the local (stdio) server only, as file_paths; the hosted one takes urls or asset_ids | files and whole directories | a path or the bytes | binary data from the node before it |
| a result with nothing stored | transform on one file or URL with a deterministic op | imagestep image resize and every other synchronous op | images.transform · POST /api/v1/images/transform | Store Result off |
| cancel or resume a job | none | imagestep jobs cancel · imagestep jobs resume | jobs.cancel · jobs.resume | none |
| hear that a job ended | wait on the call, then poll job_status | --wait, or imagestep webhook create | a webhook, checked with webhooks.verify | the ImageStep Trigger node |
| save a chain as a preset | save_preset | imagestep preset create | presets.create · POST /api/v1/presets | — (it runs presets) |
The operating contract
One document, the same on every surface: GET https://api.imagestep.dev/api/v1/agent-guidelines (no key), the MCP resource imagestep://agent-guidelines — which the server also sends as its instructions when a session opens — and imagestep guidelines in the CLI. It is fetched live, so a client released months ago still reads today's wording. Its sections, in order, with what each asks:
| rule | what the agent does |
|---|---|
| Read the catalogue before you choose an op | GET /api/v1/ops is the vocabulary and the parameter contract. An op that is not there does not exist; a parameter that is not there is a 400 invalid_param. |
| Price before you spend | POST /api/v1/jobs?dryRun=true is the exact cost of the exact body. sufficientCredit: false means stop and tell the person — no change to the request fixes an account. |
| A chain you ran twice is a preset | Save it with POST /api/v1/presets and run it by slug@version; a consistent character or product is a preset with subjects, not a longer prompt. |
| Jobs own the retry; you do not | Anything with a model in it is a job. A deterministic op may answer synchronously (its syncEndpoint). Every write carries an Idempotency-Key. |
Branch on retryable, not on the status code | Fix what param names; retry what is transient; resume a retryable failed job once; pace from RateLimit-Remaining. |
| References, not bytes | Upload once or hand the service a URL, keep the asset id, hand on the publicUrl. An image in a context window is tokens spent on something the agent cannot use. |
| A missing capability is a report, not a detour | POST /api/v1/feedback with kind: capability_gap, and a word to the person — not a local image library quietly substituted. |
| Leave the human able to take over | The preset saved and named, the cost known before it was spent, the failure classified, the gap reported. |
Read it once per session:
JavaScript
const { markdown } = await client.agent.guidelines();Python
markdown = client.agent.guidelines()["markdown"]CLI
imagestep guidelinescurl
curl -s https://api.imagestep.dev/api/v1/agent-guidelinesMCP
# resource read — the server also sends the rules as its instructions when a session opens
imagestep://agent-guidelinesThe loop, in any surface
The rules above as calls — the same seven steps whichever door the agent came in by:
| step | REST | CLI | MCP |
|---|---|---|---|
| discover | GET /api/v1/ops | imagestep ops list -o json | resource imagestep://ops |
| get the input in | POST /api/v1/assets/upload · POST /api/v1/assets/from-url | imagestep asset upload · imagestep asset from-url | file_paths or urls on transform and run_preset |
| price | POST /api/v1/jobs?dryRun=true | imagestep jobs estimate | dry_run |
| run | POST /api/v1/jobs with an Idempotency-Key | imagestep jobs submit --wait | transform · generate · run_preset, with idempotency_key |
| wait | wait on the submit, GET /api/v1/jobs/{id}?wait=, or a webhook | --wait · imagestep jobs wait | wait_seconds, then job_status |
| hand on the result | POST /api/v1/assets/update with published: true | imagestep asset publish | publish (on by default) — each output comes back with its public URL |
| report a gap | POST /api/v1/feedback | imagestep feedback send | send_feedback |
Machine-readable faces
Everything below answers without a key, so an agent can go from one URL to a priced call without a person reading a docs page.
- https://api.imagestep.dev/api/v1/agent-guidelines — the operating contract: read the catalogue, price before you spend, branch on retryable, report a gap instead of routing around it
- https://api.imagestep.dev/api/v1/ops — every atomic op with its parameter contract, pricing model and sync endpoint
- https://api.imagestep.dev/api/pricing/image-models — every image model with its per-image USD price
- https://api.imagestep.dev/v3/api-docs/api-key-accessible — the OpenAPI document every SDK is generated from
- /.well-known/api-catalog — RFC 9727 linkset pointing at the OpenAPI document
- /.well-known/agent-skills/index.json — the published SKILL.md set — CLI-first, for an agent with a shell (`imagestep skill --install claude-code` writes the same file) — each with a SHA-256 of the exact bytes served
- /.well-known/mcp/server-card.json — MCP server card — transport, URL, tools
- /llms.txt — what ImageStep is, in one fetch
The vocabulary — the op catalogue, which needs no key:
JavaScript
const ops = await client.ops.list(); // [{ op, kind, params, defaultModel, syncEndpoint, example, … }]Python
ops = client.ops.list() # [{"op", "kind", "params", "defaultModel", "syncEndpoint", "example", …}]CLI
imagestep ops list -o jsoncurl
curl -s https://api.imagestep.dev/api/v1/opsMCP
# resource read — one op, with its full contract, is imagestep://ops/{op}
imagestep://opsAnd the site itself in one fetch, for an agent that starts from a URL: https://imagestep.dev/llms.txt — a static file, so it has no SDK or CLI form. Every docs page is also served as markdown at its own URL plus .md, and all of them together at https://imagestep.dev/llms-full.txt.
What the person sees
Every job an agent runs is on the console's /jobs page — what op, which preset version, which model, what it cost, which item failed and why — and every asset it made is on /assets with its lineage. Keys are issued and revoked on /keys; spend by op, key or day is /usage and GET /api/v1/usage, and every gap an agent reported with POST /api/v1/feedback is listed on the same page. The console exists for the two things an API cannot do — hand out a credential and let a person check what the program did — and nothing an agent needs is only there (the console, page by page).