---
title: REST API reference
url: https://base_url.placeholder/docs/api
group: Surfaces
---

# REST API reference

53 endpoints in 11 groups, generated from the service as OpenAPI 3.1.0 — the same document the [SDKs](https://base_url.placeholder/docs/sdk) are typed from and the [MCP server](https://base_url.placeholder/docs/mcp) enumerates. Base URL `https://api.imagestep.dev`. Most programs call it through an SDK, the [CLI](https://base_url.placeholder/docs/cli) or MCP; these pages are for writing the HTTP yourself, and for checking what a surface sent.

## What every call shares

Stated here once, so the group pages do not repeat it; each row links the page that explains it in full.

|   | The rule | In full |
| --- | --- | --- |
| Authentication | `Authorization: ApiKey is_sk_…` on every call. The op catalogue answers without one. A key cannot manage keys; it can only revoke itself. | [Authentication](https://base_url.placeholder/docs/errors#auth) |
| Responses | JSON, `{success, data, error, meta}`: `data` exactly on success, `error` exactly on failure. The exceptions say so on their section — an image back from the synchronous endpoints, a redirect from `/content`, an empty `204`. | [The envelope](https://base_url.placeholder/docs/errors#envelope) |
| Errors | `error.code` from a closed set and `retryable` — branch on `retryable`, not on the status. | [Error codes](https://base_url.placeholder/docs/errors#codes) |
| Idempotency | A write marked _Accepts an Idempotency-Key_ replays the first answer to the same key and body for 24 hours; the same key with another body is `409 idempotency_key_reuse`, and one still running is `409 request_in_progress` (retryable). | [Idempotency keys](https://base_url.placeholder/docs/errors#idempotency) |
| Paging | A list marked _Paged_ takes `page` (from 0) and `perPage` (at most 100, clamped), and answers `meta`; send `meta.nextCursor` back as `cursor` to read on. | [Pagination](https://base_url.placeholder/docs/errors#pagination) |
| Rate limits | `RateLimit-Limit`, `-Remaining` and `-Reset` on every response; over it, `429 rate_limited` with `Retry-After`. | [Rate limits and quotas](https://base_url.placeholder/docs/errors#limits) |
| Request ids | `X-Request-Id` on every response, repeated as `error.requestId`. Quote it when something fails. | [Request ids](https://base_url.placeholder/docs/errors#requestid) |

And the failures no section lists, because any endpoint can answer them — the request refused before a handler ran, the missing credential, the rate limit, and the unclassified error:

| Status | Code | Retryable |
| --- | --- | --- |
| 400 | invalid_param | no |
| 401 | unauthorized | no |
| 405 | method_not_allowed | no |
| 406 | not_acceptable | no |
| 413 | payload_too_large | no |
| 415 | unsupported_media_type | no |
| 429 | rate_limited | yes |
| 500 | internal_error | yes |

## Every endpoint

One page per group, one section per endpoint. The search box finds an endpoint by path or by a word in its summary, too.

### [Assets](https://base_url.placeholder/docs/api/assets)

The images you store. Three ways in — one call with the bytes as the body, the three-step upload for large files and batches, or a public URL this service fetches — then search, collections and tags to find them again, publishing for a public URL, and delete. A job never rewrites an asset: what it makes is a new one.

/api/v1/assets

- [GET /api/v1/assets](https://base_url.placeholder/docs/api/assets#get-assets) — List and search assets
- [GET /api/v1/assets/collections](https://base_url.placeholder/docs/api/assets#get-assets-collections) — List your collections
- [POST /api/v1/assets/collections/rename](https://base_url.placeholder/docs/api/assets#post-assets-collections-rename) — Rename a collection
- [POST /api/v1/assets/delete](https://base_url.placeholder/docs/api/assets#post-assets-delete) — Batch delete assets permanently
- [POST /api/v1/assets/finish-upload](https://base_url.placeholder/docs/api/assets#post-assets-finish-upload) — Finalize file uploads
- [POST /api/v1/assets/from-url](https://base_url.placeholder/docs/api/assets#post-assets-from-url) — Ingest images from URLs
- [POST /api/v1/assets/preview-urls](https://base_url.placeholder/docs/api/assets#post-assets-preview-urls) — Sign preview URLs for a page of assets
- [POST /api/v1/assets/stage-upload](https://base_url.placeholder/docs/api/assets#post-assets-stage-upload) — Stage file upload
- [POST /api/v1/assets/status](https://base_url.placeholder/docs/api/assets#post-assets-status) — Poll the ingest status of up to 100 assets
- [POST /api/v1/assets/update](https://base_url.placeholder/docs/api/assets#post-assets-update) — Batch update asset properties
- [POST /api/v1/assets/upload](https://base_url.placeholder/docs/api/assets#post-assets-upload) — Upload one image
- [GET /api/v1/assets/{id}](https://base_url.placeholder/docs/api/assets#get-assets-id) — Get asset by ID
- [DELETE /api/v1/assets/{id}](https://base_url.placeholder/docs/api/assets#delete-assets-id) — Delete an asset file permanently
- [GET /api/v1/assets/{id}/content](https://base_url.placeholder/docs/api/assets#get-assets-id-content) — Download asset content

### [Jobs](https://base_url.placeholder/docs/api/jobs)

Asynchronous work: an op, a saved preset or inline steps over a batch of assets, run as one job with a handle. Price it with a dry run, wait on it in the same call or follow it by id or webhook, cancel it, resume what failed. Credits are charged when the job settles, for what completed.

/api/v1/jobs

- [GET /api/v1/jobs](https://base_url.placeholder/docs/api/jobs#get-jobs) — List jobs
- [POST /api/v1/jobs](https://base_url.placeholder/docs/api/jobs#post-jobs) — Submit a new job
- [GET /api/v1/jobs/counts](https://base_url.placeholder/docs/api/jobs#get-jobs-counts) — Count jobs by status and type
- [GET /api/v1/jobs/{id}](https://base_url.placeholder/docs/api/jobs#get-jobs-id) — Get a job
- [POST /api/v1/jobs/{id}/cancel](https://base_url.placeholder/docs/api/jobs#post-jobs-id-cancel) — Cancel a job
- [GET /api/v1/jobs/{id}/items](https://base_url.placeholder/docs/api/jobs#get-jobs-id-items) — List a job's items
- [POST /api/v1/jobs/{id}/resume](https://base_url.placeholder/docs/api/jobs#post-jobs-id-resume) — Resume incomplete items in a job

### [Ops](https://base_url.placeholder/docs/api/ops)

The catalogue of atomic operations: what `POST /api/v1/jobs` accepts in `op`, with each op's parameter contract, its price as data, an example request and the endpoint that runs it synchronously, if any. Read it instead of carrying a list of op names. AI ops charge credits per item; deterministic ops count against the plan's processing allowance; a sync op names the endpoint that answers it. **No credential needed** — an agent can read the catalogue before it has a key.

/api/v1/ops

- [GET /api/v1/ops](https://base_url.placeholder/docs/api/ops#get-ops) — List every atomic op with its parameter contract
- [GET /api/v1/ops/{op}](https://base_url.placeholder/docs/api/ops#get-ops-op) — One op's contract

### [Models](https://base_url.placeholder/docs/api/models)

The models the AI ops run on — what `model` may name on a job or a preset step, with each model's price, its categories and how big an image it is handed

/api/v1/ai-models

- [GET /api/v1/ai-models](https://base_url.placeholder/docs/api/models#get-ai-models) — List AI models

### [Presets](https://base_url.placeholder/docs/api/presets)

Presets: named, versioned lists of steps — atomic ops and processing-registry steps — saved once and run by slug, id or slug@version

/api/v1/presets

- [GET /api/v1/presets](https://base_url.placeholder/docs/api/presets#get-presets) — List presets
- [POST /api/v1/presets](https://base_url.placeholder/docs/api/presets#post-presets) — Create a preset
- [POST /api/v1/presets/import](https://base_url.placeholder/docs/api/presets#post-presets-import) — Import presets
- [GET /api/v1/presets/{slug}](https://base_url.placeholder/docs/api/presets#get-presets-slug) — Get a preset, or one earlier version of it
- [PUT /api/v1/presets/{slug}](https://base_url.placeholder/docs/api/presets#put-presets-slug) — Update a preset — a new version when its steps or subjects change
- [DELETE /api/v1/presets/{slug}](https://base_url.placeholder/docs/api/presets#delete-presets-slug) — Delete a preset
- [DELETE /api/v1/presets/{slug}/versions/{version}](https://base_url.placeholder/docs/api/presets#delete-presets-slug-versions-version) — Delete one superseded version

### [Templates](https://base_url.placeholder/docs/api/templates)

HTML/CSS render templates for the render_template op: create, version, import/export

/api/v1/templates

- [GET /api/v1/templates](https://base_url.placeholder/docs/api/templates#get-templates) — List templates
- [POST /api/v1/templates](https://base_url.placeholder/docs/api/templates#post-templates) — Create a template
- [POST /api/v1/templates/import](https://base_url.placeholder/docs/api/templates#post-templates-import) — Import templates
- [GET /api/v1/templates/{id}](https://base_url.placeholder/docs/api/templates#get-templates-id) — Get a template (or one frozen version of it)
- [PUT /api/v1/templates/{id}](https://base_url.placeholder/docs/api/templates#put-templates-id) — Update a template (new version)
- [DELETE /api/v1/templates/{id}](https://base_url.placeholder/docs/api/templates#delete-templates-id) — Delete a template
- [GET /api/v1/templates/{id}/versions](https://base_url.placeholder/docs/api/templates#get-templates-id-versions) — List a template's versions

### [Synchronous images](https://base_url.placeholder/docs/api/images)

Run a deterministic operation while you wait. Nothing is stored.

/api/v1/images

- [POST /api/v1/images/metadata](https://base_url.placeholder/docs/api/images#post-images-metadata) — Read an image's metadata synchronously
- [POST /api/v1/images/render](https://base_url.placeholder/docs/api/images#post-images-render) — Render one template row synchronously
- [POST /api/v1/images/transform](https://base_url.placeholder/docs/api/images#post-images-transform) — Transform an image synchronously

### [Webhook endpoints](https://base_url.placeholder/docs/api/webhooks)

Register URLs to receive job events instead of polling, and read back what was delivered. At most 10 endpoints per account. Every delivery is signed with the endpoint's secret and retried until your receiver answers `2xx` or 24 hours have passed; the events, their bodies and how to verify a signature are on /docs/webhooks.

/api/v1/webhook-endpoints

- [GET /api/v1/webhook-endpoints](https://base_url.placeholder/docs/api/webhooks#get-webhook-endpoints) — List webhook endpoints
- [POST /api/v1/webhook-endpoints](https://base_url.placeholder/docs/api/webhooks#post-webhook-endpoints) — Register a webhook endpoint
- [GET /api/v1/webhook-endpoints/{id}](https://base_url.placeholder/docs/api/webhooks#get-webhook-endpoints-id) — Get one webhook endpoint
- [PUT /api/v1/webhook-endpoints/{id}](https://base_url.placeholder/docs/api/webhooks#put-webhook-endpoints-id) — Update a webhook endpoint
- [DELETE /api/v1/webhook-endpoints/{id}](https://base_url.placeholder/docs/api/webhooks#delete-webhook-endpoints-id) — Delete a webhook endpoint
- [GET /api/v1/webhook-endpoints/{id}/deliveries](https://base_url.placeholder/docs/api/webhooks#get-webhook-endpoints-id-deliveries) — Recent deliveries to an endpoint
- [POST /api/v1/webhook-endpoints/{id}/rotate-secret](https://base_url.placeholder/docs/api/webhooks#post-webhook-endpoints-id-rotate-secret) — Rotate the signing secret
- [POST /api/v1/webhook-endpoints/{id}/test](https://base_url.placeholder/docs/api/webhooks#post-webhook-endpoints-id-test) — Send a test delivery

### [Usage](https://base_url.placeholder/docs/api/usage)

What this account spent over a window — credits, jobs, items and synchronous calls — grouped by op, API key or day: the number an agent holds a budget against

/api/v1/usage

- [GET /api/v1/usage](https://base_url.placeholder/docs/api/usage#get-usage) — Usage over a window, grouped

### [Feedback](https://base_url.placeholder/docs/api/feedback)

The agent contract face. `GET /api/v1/agent-guidelines` is the operating rules as one document — public, no key needed. `POST /api/v1/feedback` reports what ImageStep could not do, instead of routing around it, and `GET /api/v1/feedback` reads your reports back.

/api/v1/feedback

- [GET /api/v1/feedback](https://base_url.placeholder/docs/api/feedback#get-feedback) — Your own reports
- [POST /api/v1/feedback](https://base_url.placeholder/docs/api/feedback#post-feedback) — Report something ImageStep could not do

### [API keys](https://base_url.placeholder/docs/api/api-keys)

API keys are issued, listed and revoked from a signed-in console session: a key cannot manage credentials, because one that could mint more could outlive its own revocation. The one exception points the other way — a key may revoke itself.

/api/v1/api-keys/self

- [DELETE /api/v1/api-keys/self](https://base_url.placeholder/docs/api/api-keys#delete-api-keys-self) — Revoke the API key used for this request

## The interactive reference

The same document is served by the API itself at `https://api.imagestep.dev/docs` and [opens standalone](https://imagestep_service_url.placeholder/docs) — a console with a request builder and a schema browser, for when you want to send a call rather than read about one. The pages here are the readable half: one URL per endpoint, indexed by the [docs search](https://base_url.placeholder/docs) and by whatever your agent uses.
