---
title: For agents
url: https://base_url.placeholder/docs/agents
group: Surfaces
---

# 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](https://base_url.placeholder/docs/mcp) | 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](https://base_url.placeholder/docs/skill) \+ [the CLI](https://base_url.placeholder/docs/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](https://base_url.placeholder/docs/sdk), [REST](https://base_url.placeholder/docs/api) or [the n8n node](https://base_url.placeholder/docs/n8n) | 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:

|   | 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 | — | `imagestep jobs cancel` · `imagestep jobs resume` | `jobs.cancel` · `jobs.resume` | — |
| 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

```js
const { markdown } = await client.agent.guidelines();
```

#### Python

```python
markdown = client.agent.guidelines()["markdown"]
```

#### CLI

```sh
imagestep guidelines
```

#### curl

```sh
curl -s https://api.imagestep.dev/api/v1/agent-guidelines
```

#### MCP

```text
# resource read — the server also sends the rules as its instructions when a session opens
imagestep://agent-guidelines
```

## The 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](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](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](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](https://api.imagestep.dev/v3/api-docs/api-key-accessible) — the OpenAPI document every SDK is generated from
- [/.well-known/api-catalog](https://base_url.placeholder/.well-known/api-catalog) — RFC 9727 linkset pointing at the OpenAPI document
- [/.well-known/agent-skills/index.json](https://base_url.placeholder/.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](https://base_url.placeholder/.well-known/mcp/server-card.json) — MCP server card — transport, URL, tools
- [/llms.txt](https://base_url.placeholder/llms.txt) — what ImageStep is, in one fetch

The vocabulary — the op catalogue, which needs no key:

#### JavaScript

```js
const ops = await client.ops.list(); // [{ op, kind, params, defaultModel, syncEndpoint, example, … }]
```

#### Python

```python
ops = client.ops.list()  # [{"op", "kind", "params", "defaultModel", "syncEndpoint", "example", …}]
```

#### CLI

```sh
imagestep ops list -o json
```

#### curl

```sh
curl -s https://api.imagestep.dev/api/v1/ops
```

#### MCP

```text
# resource read — one op, with its full contract, is imagestep://ops/{op}
imagestep://ops
```

And 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](https://base_url.placeholder/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](https://base_url.placeholder/assets) with its lineage. Keys are issued and revoked on [/keys](https://base_url.placeholder/keys); spend by op, key or day is [/usage](https://base_url.placeholder/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](https://base_url.placeholder/docs/console)).
