Ironfang Destinations API

Where a finished job goes - a signed webhook, or your own S3 bucket.

Operations 9

POST /v1/destinations Register a delivery destination #
GET /v1/destinations List destinations #
GET /v1/destinations/{id} Get a destination #
PATCH /v1/destinations/{id} Rename or enable a destination #
DELETE /v1/destinations/{id} Remove a destination #
POST /v1/destinations/{id}/test Test a destination now #
GET /v1/deliveries List deliveries #
GET /v1/deliveries/{id} One delivery, with the body it posted #
POST /v1/deliveries/{id}/redeliver Send a delivery again #

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/ironfang:ironfang-destinations-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

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

ironfang-destinations-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Ironfang Render Destinations API
  version: 1.0.0
  summary: Screenshots, PDFs, dynamic images and video through one API.
  description: Ironfang Render renders web pages to images, PDFs and video.
  contact:
    name: Ironfang
    email: hello@ironfang.uk
    url: https://ironfang.com
  termsOfService: https://ironfang.com/legal/terms
  license:
    name: Proprietary - use governed by the Ironfang terms of service
    url: https://ironfang.com/legal/terms
servers:
- url: https://api.ironfang.com/render
  description: Production
- url: https://api.ironfang.com
  description: Production, unpartitioned - retained for existing clients
security:
- apiKey: []
tags:
- name: Destinations
  description: Where a finished job goes - a signed webhook, or your own S3 bucket.
paths:
  /v1/destinations:
    post:
      tags:
      - Destinations
      summary: Register a delivery destination
      description: 'A destination is created once and named by id from every job, so a

        signing secret or a set of S3 keys is sent to us on one request and

        never appears in a job body again.


        A `webhook` destination needs a public `https` URL. The response

        carries `signing_secret` **once**: we keep only an encrypted copy,

        so there is no endpoint that can show it to you again.


        An `s3` destination needs `bucket`, `region`, `access_key` and

        `secret_key`; `endpoint` (for a non-AWS provider), `path_style` and

        `prefix` are optional. Scope the credentials to the prefix you give

        us. They are never returned after creation.


        Needs the `render:destinations` scope, which maps to the

        `destinations.manage` permission.'
      operationId: createDestination
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - type
              properties:
                type:
                  type: string
                  enum:
                  - webhook
                  - s3
                name:
                  type: string
                  maxLength: 120
                url:
                  type: string
                  format: uri
                  description: 'webhook: a public https URL.'
                endpoint:
                  type: string
                  format: uri
                region:
                  type: string
                bucket:
                  type: string
                prefix:
                  type: string
                  maxLength: 256
                path_style:
                  type: boolean
                access_key:
                  type: string
                secret_key:
                  type: string
                session_token:
                  type: string
      responses:
        '201':
          description: The destination. A webhook also carries `signing_secret`, shown once.
        '400':
          description: '`bad_destination`: not a public https URL, or missing bucket credentials.'
        '403':
          description: The key carries no `render:destinations` scope.
        '501':
          description: This deployment has no credential vault configured.
    get:
      tags:
      - Destinations
      summary: List destinations
      description: Live destinations, newest first. Secrets and credentials are never included.
      operationId: listDestinations
      responses:
        '200':
          description: The account's destinations.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: The key carries no `render:destinations` scope, or is not account-backed.
  /v1/destinations/{id}:
    get:
      tags:
      - Destinations
      summary: Get a destination
      operationId: getDestination
      description: Returns a delivery destination belonging to the authenticated account. Stored delivery credentials are not included in the response.
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The destination.
        '404':
          description: No such destination on this account.
    patch:
      tags:
      - Destinations
      summary: Rename or enable a destination
      description: '`name` and `enabled` only. An address or a credential is not edited in

        place - create a new destination and point your jobs at it, so a

        delivery''s history always says where it actually went. Re-enabling

        clears the failure count.'
      operationId: updateDestination
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  maxLength: 120
                enabled:
                  type: boolean
      responses:
        '200':
          description: The destination.
        '404':
          description: No such destination on this account.
    delete:
      tags:
      - Destinations
      summary: Remove a destination
      description: 'It stops receiving anything and leaves the list. Its past deliveries

        stay, so history still reads true.'
      operationId: deleteDestination
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '204':
          description: Removed.
        '404':
          description: No such destination on this account.
  /v1/destinations/{id}/test:
    post:
      tags:
      - Destinations
      summary: Test a destination now
      description: 'A webhook receives a signed `render.destination.test` event; a

        bucket has a uniquely named object written to it. Always `200`: the

        body''s `ok` says whether the destination answered, and `error` says

        what went wrong if it did not.'
      operationId: testDestination
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: '`ok` and, on failure, `error`.'
        '404':
          description: No such destination on this account.
  /v1/deliveries:
    get:
      tags:
      - Destinations
      summary: List deliveries
      description: 'Newest first, optionally for one `destination`. A delivery is

        `pending`, `delivering`, `delivered` or `failed`; `failed` means it

        used up its attempts. Delivery state never affects the job''s own

        outcome. Filters apply before the cursor boundary. `pending` selects

        pending deliveries with no attempts; `retrying` selects pending deliveries

        with at least one attempt. `job_id` selects one owned job.'
      operationId: listDeliveries
      parameters:
      - name: destination
        in: query
        schema:
          type: string
          format: uuid
      - name: job_id
        in: query
        schema:
          type: string
          format: uuid
      - name: status
        in: query
        schema:
          type: string
          enum:
          - pending
          - retrying
          - delivering
          - delivered
          - failed
      - name: limit
        in: query
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 50
      - name: cursor
        in: query
        schema:
          type: string
      responses:
        '200':
          description: Deliveries and next_cursor, with no-store caching.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeliveryList'
        '400':
          description: Invalid, repeated or unknown query parameter.
  /v1/deliveries/{id}:
    get:
      tags:
      - Destinations
      summary: One delivery, with the body it posted
      description: 'The list omits payloads because they are the largest part of a

        delivery and are wanted one at a time, when something has gone wrong.

        This returns the body exactly as it went on the wire, plus

        `payload_timestamp` and `payload_signature` - the two values the

        signature is computed over - so you can reproduce the HMAC check

        against your own secret and see where it diverges.


        `response_body` is what your endpoint sent back, bounded to 4 KB.


        Bodies are kept for `payload_retention_days` (7) after the attempt

        and then blanked; the delivery row itself stays, so the history of

        what was sent and how it went is not lost with it.'
      operationId: getDelivery
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The delivery. Payload is absent once purged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeliveryRecord'
        '404':
          description: No such delivery on this account.
  /v1/deliveries/{id}/redeliver:
    post:
      tags:
      - Destinations
      summary: Send a delivery again
      description: 'Puts a delivery back on the queue with a fresh attempt ladder, for

        after you have fixed whatever was wrong. The webhook body is rebuilt

        at send time, so the result link in it is good for 15 minutes from

        then rather than from the original attempt.'
      operationId: redeliverDelivery
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: The delivery, queued again.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeliveryRecord'
        '404':
          description: No such delivery on this account.
components:
  schemas:
    DeliveryList:
      type: object
      additionalProperties: false
      required:
      - deliveries
      - next_cursor
      properties:
        deliveries:
          type: array
          maxItems: 100
          items:
            $ref: '#/components/schemas/DeliveryRecord'
        next_cursor:
          type: string
          maxLength: 256
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: object
          required:
          - code
          - message
          properties:
            code:
              type: string
              description: 'Stable, safe to branch on. New codes may be added: treat a

                code you do not recognise as a failure of the class its HTTP

                status indicates.

                '
              enum:
              - bad_request
              - unauthorized
              - invalid_api_key
              - auth_failed
              - forbidden
              - billing_moved
              - not_found
              - rate_limited
              - target_rate_limited
              - render_failed
              - bad_signature
              - signing_disabled
              - account_gone
              - account_not_linked
              - account_required
              - admission_failed
              - bad_destination
              - bad_target
              - batch_plan_failed
              - batch_submit_failed
              - batch_too_large
              - bundle_failed
              - clip_busy
              - concurrency_limit
              - delegated_not_allowed
              - delivery_disabled
              - delivery_plan_failed
              - destination_disabled
              - idempotency_conflict
              - idempotency_key_used
              - insufficient_scope
              - invalid_token
              - job_finished
              - job_plan_failed
              - job_submit_failed
              - logo_fetch_failed
              - pdf_too_long
              - preview_busy
              - queue_full
              - result_expired
              - result_not_ready
              - result_read_failed
              - secret_failed
              - site_preview_disabled
              - store_failed
              - unknown_destination
              - unscannable
              - vault_failed
              - video_disabled
              - free_allowance_exhausted
              - account_budget_exhausted
              - product_budget_exhausted
              - paid_usage_paused
              - payment_required
              - payment_action_required
              - account_restricted
              - account_closed
              - product_not_eligible
              - exemption_limit_reached
              - operation_too_large
              - billing_temporarily_unavailable
            message:
              type: string
              description: Human-readable. May change; do not match on it.
            docs:
              type: string
              format: uri
              description: Where the error codes are documented.
        request_id:
          type: string
          description: The X-Ironfang-Request-ID of the failed request. Quote it to support.
      example:
        error:
          code: target_rate_limited
          message: too many renders for that host, try again shortly
          docs: https://ironfang.com/render/docs#errors
        request_id: 01992f6e-7c1a-7b3e-9d52-4f0a6c8e2b17
    DeliveryRecord:
      type: object
      additionalProperties: false
      required:
      - id
      - job_id
      - destination_id
      - event
      - status
      - attempts
      - created_at
      properties:
        id:
          type: string
          format: uuid
        job_id:
          type: string
          format: uuid
        destination_id:
          type: string
          format: uuid
        event:
          type: string
        status:
          type: string
          enum:
          - pending
          - delivering
          - delivered
          - failed
        attempts:
          type: integer
          minimum: 0
        created_at:
          type: string
          format: date-time
        next_attempt_at:
          type: string
          format: date-time
        delivered_at:
          type: string
          format: date-time
        response_status:
          type: integer
        error:
          type: string
        object_key:
          type: string
        payload:
          type: string
          description: Last posted body; omitted from lists and after purging.
        payload_timestamp:
          type: string
        payload_signature:
          type: string
        payload_at:
          type:
          - string
          - 'null'
          format: date-time
        payload_retention_days:
          type: integer
        response_body:
          type: string
  responses:
    Unauthorized:
      description: Missing, malformed, revoked or unknown API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: 'An API key from the portal, sent as `Authorization: Bearer rw_live_...`.

        '
externalDocs:
  description: Ironfang Render documentation
  url: https://ironfang.com/render/docs