ImageStep Agent API

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.

GitHub OpenAPI

Operations 2

GET /api/v1/feedback Your own reports #
POST /api/v1/feedback Report something ImageStep could not do #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/imagestep-dev:imagestep-dev-agent-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

imagestep-dev-agent-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: ImageStep Agent API
  description: 'The image step for your automations. Store images as assets, run an op or a preset on them as a job — priced first with `?dryRun=true`, then read, waited on or delivered to a webhook — or transform one image synchronously while you wait. Send `Authorization: ApiKey `; the op catalogue (`/api/v1/ops`) needs no credential.


    **Idempotency:** Endpoints that accept an `Idempotency-Key` header support optional deduplication. Supply a unique key (e.g. a UUID) per logical request. Repeating the same key with the same body replays the stored response (`Idempotency-Replayed: true`) without re-running anything; repeating it with a different body is a 409 `idempotency_key_reuse`. Omitting the header processes the request normally. Keys are retained for 24 hours.


    **Errors** all share one shape: `{"error": {"code", "message", "retryable", "param", "details", "requestId"}}`. Branch on `code`, and let `retryable` — not the status code — decide whether to come back.


    **Rate limits** are published on every response as `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` (seconds). A 429 adds `Retry-After`.


    **A paged list** takes `page` (from 0) and `perPage`, and every page answers `meta.nextCursor`; send it back as `cursor` to read on without counting.


    **Every response carries `X-Request-Id`** — yours if you sent one, ours otherwise — and a failure repeats it as `error.requestId`. It is what finds the request in our logs.


    The error-code table, idempotency, limits and pagination are at https://imagestep.dev/docs/errors; the webhook events at https://imagestep.dev/docs/webhooks; what may change inside v1, and the changelog, at https://imagestep.dev/docs/changelog.'
  contact:
    name: ImageStep Team
    url: https://imagestep.dev/support
  license:
    name: Proprietary
    url: https://imagestep.dev/terms
  version: v1
servers:
- url: https://api.imagestep.dev
  description: Production server
tags:
- name: Agent
  description: 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.
paths:
  /api/v1/feedback:
    get:
      tags:
      - Agent
      summary: Your own reports
      description: Everything this account has submitted, newest first, a page at a time. Yours only; there is no cross-account read.
      operationId: list_4
      parameters:
      - name: page
        in: query
        description: Page number (0-indexed). Leave it out when you send cursor
        required: false
        schema:
          type: integer
          format: int32
          default: 0
      - name: perPage
        in: query
        description: Items per page (max 100; out of range is clamped)
        required: false
        schema:
          type: integer
          format: int32
          default: 100
      - name: cursor
        in: query
        description: 'The meta.nextCursor of the previous page: the rows after it, read without counting — the way to walk a long listing. Send it unchanged, with the same filters; it cannot be combined with page'
        required: false
        schema:
          type: string
      responses:
        '200':
          description: OK
          headers:
            RateLimit-Limit:
              description: Requests allowed in the current window
              schema:
                type: integer
                format: int32
            RateLimit-Remaining:
              description: Requests left in the current window
              schema:
                type: integer
                format: int32
            RateLimit-Reset:
              description: Seconds until the window resets
              schema:
                type: integer
                format: int32
            X-Request-Id:
              description: This request's id — echoed from your own `X-Request-Id` when you send one. Identical to `error.requestId` on a failure; quote it when reporting one.
              schema:
                type: string
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/ApiResponseListFeedback'
        '429':
          description: Rate limited — `error.code` is `rate_limited` and `retryable` is true. `Retry-After` says how many seconds to wait.
          headers:
            Retry-After:
              description: Seconds to wait before retrying
              schema:
                type: integer
                format: int32
            RateLimit-Limit:
              description: Requests allowed in the current window
              schema:
                type: integer
                format: int32
            RateLimit-Remaining:
              description: Requests left in the current window
              schema:
                type: integer
                format: int32
            RateLimit-Reset:
              description: Seconds until the window resets
              schema:
                type: integer
                format: int32
            X-Request-Id:
              description: This request's id — echoed from your own `X-Request-Id` when you send one. Identical to `error.requestId` on a failure; quote it when reporting one.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
      - apiKeyAuth: []
    post:
      tags:
      - Agent
      summary: Report something ImageStep could not do
      description: 'USE THIS WHEN: an op you needed does not exist, a parameter did not behave the

        way the contract describes, or you were about to work around ImageStep with

        something local. Reporting the gap is the alternative to routing around it.


        Free: no credit, no job, no rate-limit budget beyond the shared API one, and at most 200 reports per account per UTC day — past that, `429 rate_limited` until midnight UTC.

        `kind` is `capability_gap` (ImageStep cannot do it at all), `bug` (it did not

        behave the way the contract says) or `other`; `message` says what you were trying

        to do. `op` names the operation when there is one and is **not** checked against

        the catalogue — the most useful report is about an op that does not exist yet.

        `context` is any JSON object worth keeping.


        The report comes back (`201`) with the `apiKeyId` it was filed under, so an account

        running one key per agent can tell whose report it is. It is your account''s data:

        exported with it, and deleted with it or 365 days after it was filed, whichever comes first.'
      operationId: submit_1
      parameters:
      - name: Idempotency-Key
        in: header
        description: 'Optional. A client-generated unique key (typically a UUID). Same key + the same request (method, path, query string and body) replays the first response verbatim with `Idempotency-Replayed: true`, without executing again. Same key + a different one returns 409 `idempotency_key_reuse`. A repeat that arrives while the first is still running returns 409 `request_in_progress`, which is retryable. Keys are retained 24 hours. At most 255 characters; a longer one is a 400 `invalid_param` with `param: "Idempotency-Key"`.'
        required: false
        schema:
          type: string
          maxLength: 255
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FeedbackRequest'
        required: true
      responses:
        '201':
          description: Logged
          headers:
            Idempotency-Replayed:
              description: '`true` when this is the stored answer to an earlier request with the same `Idempotency-Key`, replayed without running it again; absent otherwise'
              schema:
                type: string
                enum:
                - 'true'
            RateLimit-Limit:
              description: Requests allowed in the current window
              schema:
                type: integer
                format: int32
            RateLimit-Remaining:
              description: Requests left in the current window
              schema:
                type: integer
                format: int32
            RateLimit-Reset:
              description: Seconds until the window resets
              schema:
                type: integer
                format: int32
            X-Request-Id:
              description: This request's id — echoed from your own `X-Request-Id` when you send one. Identical to `error.requestId` on a failure; quote it when reporting one.
              schema:
                type: string
          content:
            '*/*':
              schema:
                $ref: '#/components/schemas/ApiResponseFeedback'
        '400':
          description: 'Nothing is stored when any of these is refused:

            - `invalid_param` on `kind` — missing, or not one of the three (matched case-insensitively)

            - `invalid_param` on `message` — missing, blank, or over 4000 characters once trimmed

            - `invalid_param` on `op` — over 64 characters

            - `invalid_param` on `context` — over 4000 characters once encoded as JSON'
          headers:
            RateLimit-Limit:
              description: Requests allowed in the current window
              schema:
                type: integer
                format: int32
            RateLimit-Remaining:
              description: Requests left in the current window
              schema:
                type: integer
                format: int32
            RateLimit-Reset:
              description: Seconds until the window resets
              schema:
                type: integer
                format: int32
            X-Request-Id:
              description: This request's id — echoed from your own `X-Request-Id` when you send one. Identical to `error.requestId` on a failure; quote it when reporting one.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: 'Rate limited — `error.code` is `rate_limited` and `retryable` is true. `Retry-After` says how many seconds to wait.


            Here also: `rate_limited` — this account filed 200 reports today (UTC). Retryable: `details.resetAt` is the next midnight UTC, `Retry-After` the seconds until it; nothing is stored'
          headers:
            Retry-After:
              description: Seconds to wait before retrying
              schema:
                type: integer
                format: int32
            RateLimit-Limit:
              description: Requests allowed in the current window
              schema:
                type: integer
                format: int32
            RateLimit-Remaining:
              description: Requests left in the current window
              schema:
                type: integer
                format: int32
            RateLimit-Reset:
              description: Seconds until the window resets
              schema:
                type: integer
                format: int32
            X-Request-Id:
              description: This request's id — echoed from your own `X-Request-Id` when you send one. Identical to `error.requestId` on a failure; quote it when reporting one.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Conflict — `error.code` is `idempotency_key_reuse` (the key already means a different request) or `request_in_progress` (the first request under this key has not settled yet; retryable).
          headers:
            RateLimit-Limit:
              description: Requests allowed in the current window
              schema:
                type: integer
                format: int32
            RateLimit-Remaining:
              description: Requests left in the current window
              schema:
                type: integer
                format: int32
            RateLimit-Reset:
              description: Seconds until the window resets
              schema:
                type: integer
                format: int32
            X-Request-Id:
              description: This request's id — echoed from your own `X-Request-Id` when you send one. Identical to `error.requestId` on a failure; quote it when reporting one.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
      - apiKeyAuth: []
components:
  schemas:
    ApiResponseFeedback:
      type: object
      description: Standard API response wrapper containing status, message, data and timestamp
      properties:
        success:
          type: boolean
          description: Indicates if the request was successful
          example: true
        message:
          type: string
          description: Human-readable message about the response
          example: Operation completed successfully
        data:
          $ref: '#/components/schemas/Feedback'
          description: The response data payload
        timestamp:
          type: string
          format: date-time
          description: Timestamp when the response was generated
          example: '2024-01-15T10:30:45.123Z'
        error:
          $ref: '#/components/schemas/ApiError'
          description: Structured error; present exactly when the request failed
        meta:
          $ref: '#/components/schemas/PageMeta'
          description: Pagination metadata for list responses
      required:
      - data
      - success
    ApiError:
      type: object
      description: Structured error. Present on every 4xx/5xx response, absent on success.
      properties:
        code:
          type: string
          description: Machine-readable error code from a closed set
          example: invalid_param
        message:
          type: string
          description: Human-readable explanation. Never parse this — parse `code`.
          example: 'format must be one of: jpeg, png, webp'
        retryable:
          type: boolean
          description: Whether repeating the same request unchanged could succeed. The whole reason this field exists is that an agent cannot infer it from the status code.
          example: false
        param:
          type: string
          description: Which request parameter the error is about, when it is about one
          example: format
        details:
          type: object
          additionalProperties: {}
          description: Extra machine-readable context, e.g. per-field validation messages
        requestId:
          type: string
          description: 'The id of this request, identical to the `X-Request-Id` response header. Quote it when reporting a failure: it is what finds the request in our logs.'
          example: 9f0c1c2e-6f2a-4f6b-9d3e-0a1b2c3d4e5f
      required:
      - retryable
    ErrorResponse:
      type: object
      description: 'The answer to every failed request: `error` says what went wrong and whether to retry, and nothing else rides beside it — no `data`, no `meta`.'
      properties:
        success:
          type: boolean
          description: Always `false` on a failure
        error:
          $ref: '#/components/schemas/ApiError'
        timestamp:
          type: string
          format: date-time
          description: When the response was generated
      required:
      - error
      - success
    Feedback:
      type: object
      properties:
        id:
          type: string
          description: The report's id, `fbk_…`
        kind:
          type: string
          description: What kind of report it is, stored lower-case
          enum:
          - capability_gap
          - bug
          - other
        op:
          type: string
          description: The op this is about, when it is about one.
          example: remove_bg
        apiKeyId:
          type: string
          description: The API key this was reported with; absent when it came from a signed-in console session.
        message:
          type: string
          description: What the reporter wrote, trimmed
        context:
          type: object
          additionalProperties: {}
          description: 'Anything structured worth keeping: the parameters tried, the client, the model.'
        createdAt:
          type: string
          format: date-time
          description: When it was filed
      required:
      - createdAt
      - id
      - kind
      - message
    ApiResponseListFeedback:
      type: object
      description: Standard API response wrapper containing status, message, data and timestamp
      properties:
        success:
          type: boolean
          description: Indicates if the request was successful
          example: true
        message:
          type: string
          description: Human-readable message about the response
          example: Operation completed successfully
        data:
          type: array
          description: The response data payload
          items:
            $ref: '#/components/schemas/Feedback'
        timestamp:
          type: string
          format: date-time
          description: Timestamp when the response was generated
          example: '2024-01-15T10:30:45.123Z'
        error:
          $ref: '#/components/schemas/ApiError'
          description: Structured error; present exactly when the request failed
        meta:
          $ref: '#/components/schemas/PageMeta'
          description: Pagination metadata for list responses
      required:
      - data
      - success
    FeedbackRequest:
      type: object
      description: A report from an agent about something ImageStep could not do.
      properties:
        kind:
          type: string
          description: What kind of report it is, matched case-insensitively
          enum:
          - capability_gap
          - bug
          - other
        op:
          type: string
          description: The op this is about, when it is about one — any name up to 64 characters; not checked against the catalogue.
          example: remove_bg
        message:
          type: string
          description: What you were trying to do and what stopped you; at most 4000 characters.
        context:
          type: object
          additionalProperties: {}
          description: Anything structured worth keeping — the parameters tried, the client, the model; at most 4000 characters once encoded as JSON.
      required:
      - kind
      - message
    PageMeta:
      type: object
      properties:
        total:
          type: integer
          format: int64
          description: 'Rows matching the filter. Page mode only: a cursor request counts nothing'
        page:
          type: integer
          format: int32
          description: The page served, from 0. Page mode only
        perPage:
          type: integer
          format: int32
          description: Rows per page actually used (out of range is clamped)
        hasMore:
          type: boolean
          description: Whether there is another page
        nextCursor:
          type:
          - string
          - 'null'
          description: Pass back as `cursor` for the rows after this page; null on the last page. Opaque — send it unchanged, with the same filters
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      description: 'API Key authentication. Format: ''ApiKey <your-api-key>''. Note: Only allowlisted endpoints accept API key authentication.'
      name: Authorization
      in: header