Formfeed Account API

Workspace, plan and usage.

Operations 10

POST /auth/device Start a device authorisation #
POST /auth/device/token Poll a device authorisation #
GET /account Get workspace and plan #
GET /usage Get usage for the current period #
DELETE /workspaces/{id} Delete a workspace #
GET /workspaces/{id}/storage Read the storage connection #
PUT /workspaces/{id}/storage Connect a bucket #
DELETE /workspaces/{id}/storage Disconnect the bucket #
POST /workspaces/{id}/storage/test Test the storage connection again #
POST /workspaces/{id}/storage/redeliver Deliver failed copies 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/formfeed:formfeed-account-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

formfeed-account-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Formfeed Account API
  version: 1.0.0
  description: Generate PDFs and images from templates and JSON data, one API call per document.
  termsOfService: https://formfeed.dev/legal/terms
  contact:
    name: Formfeed support
    email: support@formfeed.dev
    url: https://formfeed.dev
servers:
- url: https://api-eu.formfeed.dev/v1
  description: EU (Frankfurt), where every workspace renders and stores today
- url: https://api.formfeed.dev/v1
  description: Routes to the workspace's region
- url: https://api-us.formfeed.dev/v1
  description: Reserved for a US region, which is not open yet
security:
- bearerAuth: []
tags:
- name: Account
  description: Workspace, plan and usage.
paths:
  /auth/device:
    post:
      tags:
      - Account
      operationId: startDeviceLogin
      summary: Start a device authorisation
      description: Unauthenticated. The CLI calls this, shows `user_code`, and polls `/auth/device/token` until the person approves it in the app.
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                client_name:
                  type: string
                  maxLength: 100
      responses:
        '201':
          description: Flow started.
          content:
            application/json:
              schema:
                type: object
                properties:
                  device_code:
                    type: string
                  user_code:
                    type: string
                    example: ABCD-EFGH
                  verification_uri:
                    type: string
                    format: uri
                  verification_uri_complete:
                    type: string
                    format: uri
                  expires_in:
                    type: integer
                    example: 600
                  interval:
                    type: integer
                    example: 5
        default:
          $ref: '#/components/responses/Problem'
  /auth/device/token:
    post:
      tags:
      - Account
      operationId: claimDeviceLogin
      summary: Poll a device authorisation
      description: Unauthenticated. Answers 202 while the request is pending and 200 with the API key once it is approved. The key is handed over exactly once.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - device_code
              properties:
                device_code:
                  type: string
      responses:
        '200':
          description: Approved.
          content:
            application/json:
              schema:
                type: object
                properties:
                  api_key:
                    type: string
                    example: ff_live_3f9c...
                  workspace:
                    type: object
                    properties:
                      id:
                        type: string
                      slug:
                        type: string
                      name:
                        type: string
                      region:
                        $ref: '#/components/schemas/Region'
                  organization:
                    type: object
                    properties:
                      id:
                        type: string
                      slug:
                        type: string
                      name:
                        type: string
        '202':
          description: Still waiting for approval.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    enum:
                    - pending
                  interval:
                    type: integer
        default:
          $ref: '#/components/responses/Problem'
  /account:
    get:
      tags:
      - Account
      operationId: getAccount
      summary: Get workspace and plan
      responses:
        '200':
          description: Account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Account'
        default:
          $ref: '#/components/responses/Problem'
  /usage:
    get:
      tags:
      - Account
      operationId: getUsage
      summary: Get usage for the current period
      parameters:
      - name: period
        in: query
        description: '`current`, or `YYYY-MM` for the billing period that started in that month'
        schema:
          type: string
          default: current
      responses:
        '200':
          description: Usage.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Usage'
        default:
          $ref: '#/components/responses/Problem'
  /workspaces/{id}:
    delete:
      tags:
      - Account
      operationId: deleteWorkspace
      summary: Delete a workspace
      description: 'Deletes a workspace of the key''s organisation, for example a per-branch preview workspace your

        CI created. The workspace disappears from the app and the API at once and its API keys are

        revoked: the calling key stops working immediately when it deleted its own workspace, other

        keys within a minute. Templates, renders, stored outputs and files are removed for good

        after 30 days; there is no undo through the API. The slug can be used for a new workspace

        right away. The last workspace of an organisation is refused with 409 `last_workspace`.

        Needs `workspace:delete`, which no key has by default.'
      parameters:
      - name: id
        in: path
        required: true
        description: The workspace id (`workspace.id` of `GET /account`).
        schema:
          type: string
          format: uuid
      responses:
        '204':
          description: Deleted.
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: '`last_workspace`: the organisation has no other workspace. Delete the organisation in the app instead.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          $ref: '#/components/responses/Problem'
  /workspaces/{id}/storage:
    parameters:
    - name: id
      in: path
      required: true
      description: The workspace id.
      schema:
        type: string
        format: uuid
    get:
      tags:
      - Account
      operationId: getStorageConnection
      summary: Read the storage connection
      description: 'The bucket this workspace''s outputs are copied to (bring your own storage, Pro plan and above),

        with its deliveries of the last 24 hours. The secret is never returned, only the last four

        characters of the access key id. Owners and admins of the organisation, signed in to the app:

        no API key can manage a connection, and every storage route answers 403 to one.'
      responses:
        '200':
          description: The connection.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StorageConnectionState'
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Problem'
    put:
      tags:
      - Account
      operationId: putStorageConnection
      summary: Connect a bucket
      description: 'Creates or replaces the workspace''s connection. Formfeed writes the test object

        `.formfeed/connection-test.txt` first, with the credentials in the request, and stores

        nothing unless that works: a wrong key or a missing bucket answers 422 `storage_test_failed`

        with the provider''s error. Formfeed only ever writes (`PutObject`); it never reads, lists or

        deletes in the bucket. Leave out the credentials to keep the stored ones while the endpoint

        stays the same. From then on every output of the workspace (renders, batch items and zips,

        PDF tools, office renders) is copied to the bucket in the background; Formfeed keeps its own

        copy for the workspace retention. Owners and admins in the app only.'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StorageConnectionPut'
      responses:
        '200':
          description: Replaced.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StorageConnection'
        '201':
          description: Connected.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StorageConnection'
        '403':
          description: 'An API key or a role other than owner and admin; `forbidden` with `feature: byo_storage` below the Pro plan.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '422':
          description: '`storage_test_failed`: the test object could not be written; `error` carries the provider''s code.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          $ref: '#/components/responses/Problem'
    delete:
      tags:
      - Account
      operationId: deleteStorageConnection
      summary: Disconnect the bucket
      description: Removes the connection and its credentials. Deliveries still waiting end; objects already in the bucket stay there.
      responses:
        '204':
          description: Disconnected.
        '404':
          $ref: '#/components/responses/NotFound'
        default:
          $ref: '#/components/responses/Problem'
  /workspaces/{id}/storage/test:
    post:
      tags:
      - Account
      operationId: testStorageConnection
      summary: Test the storage connection again
      description: 'Writes the test object with the stored credentials. A connection paused after five permanent

        failures in a row resumes once this passes.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: The test passed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                    example: true
                  key:
                    type: string
                    example: formfeed/.formfeed/connection-test.txt
                  connection:
                    $ref: '#/components/schemas/StorageConnection'
        '409':
          description: '`storage_not_configured`: the workspace has no connection.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        '422':
          description: '`storage_test_failed`, with the provider''s `error` and the `connection`.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          $ref: '#/components/responses/Problem'
  /workspaces/{id}/storage/redeliver:
    post:
      tags:
      - Account
      operationId: redeliverStorage
      summary: Deliver failed copies again
      description: 'Queues every failed delivery again whose output Formfeed still keeps, for one render, one

        batch, a time range, or (with an empty body) all of them, to the connection''s current bucket

        and key. Nothing is rendered again. At most 10 000 per call; `truncated` says whether more are

        waiting. A paused connection answers 409 `conflict`: test it again first.'
      parameters:
      - name: id
        in: path
        required: true
        schema:
          type: string
          format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              properties:
                render_id:
                  type: string
                  example: rnd_7da09ab6cd9946b798c187c2a9350852
                job_id:
                  type: string
                  example: job_5f0c3b0a9d2e4c1b8a7f6e5d4c3b2a19
                since:
                  type: string
                  format: date-time
                until:
                  type: string
                  format: date-time
      responses:
        '202':
          description: Queued.
          content:
            application/json:
              schema:
                type: object
                properties:
                  requeued:
                    type: integer
                    example: 42
                  truncated:
                    type: boolean
        '409':
          description: '`storage_not_configured`, or `conflict` while the connection is paused.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          $ref: '#/components/responses/Problem'
components:
  schemas:
    StorageConnectionState:
      allOf:
      - $ref: '#/components/schemas/StorageConnection'
      - type: object
        properties:
          deliveries_24h:
            type: object
            properties:
              pending:
                type: integer
              delivered:
                type: integer
              failed:
                type: integer
    Region:
      type: string
      enum:
      - eu
      - us
      - apac
    StorageConnectionPut:
      type: object
      required:
      - provider
      - bucket
      properties:
        provider:
          type: string
          enum:
          - s3
          - r2
          - s3_compatible
          description: '`s3_compatible` covers Hetzner, IONOS, Scaleway, Wasabi, Backblaze B2, MinIO and Google Cloud Storage (HMAC keys).'
        region:
          type: string
          example: eu-central-1
          description: '`s3`: the AWS region, from which the endpoint is derived. `s3_compatible`: the region the provider signs with (`us-east-1` when omitted).'
        account_id:
          type: string
          example: 0123456789abcdef0123456789abcdef
          description: '`r2`: the Cloudflare account id.'
        jurisdiction:
          type: string
          enum:
          - default
          - eu
          default: default
          description: '`r2`: `eu` for a bucket in the EU jurisdiction.'
        endpoint:
          type: string
          format: uri
          example: https://fsn1.your-objectstorage.com
          description: '`s3_compatible`: the service address, https only.'
        force_path_style:
          type: boolean
          default: false
          description: '`s3_compatible`: address the bucket in the path instead of the host name.'
        bucket:
          type: string
          example: fennlor-docs
        prefix:
          type: string
          example: formfeed/
          description: Folder every key is written below; a trailing slash is added.
        key_template:
          type: string
          default: '{yyyy}/{mm}/{render_id}/{filename}'
          description: Object key below the prefix, with `{workspace}`, `{template}`, `{environment}`, `{render_id}`, `{job_id}`, `{yyyy}`, `{mm}`, `{dd}` and `{filename}`. A placeholder without a value (`{template}` of an HTML render, `{render_id}` of a batch zip) drops its segment.
        environments:
          type: array
          items:
            type: string
            enum:
            - live
            - test
          default:
          - live
          description: Which renders are copied; test renders only when `test` is listed.
        public_base_url:
          type:
          - string
          - 'null'
          format: uri
          example: https://docs.fennlor.example
          description: Where you serve the bucket, https only; each delivery's `url` is built from it.
        access_key_id:
          type: string
          description: Write-only. Needed for a new connection or a changed endpoint.
        secret_access_key:
          type: string
          description: Write-only; never returned.
    StorageConnection:
      type: object
      properties:
        id:
          type: string
          format: uuid
        workspace_id:
          type: string
          format: uuid
        provider:
          type: string
          enum:
          - s3
          - r2
          - s3_compatible
        endpoint:
          type: string
          format: uri
        region:
          type:
          - string
          - 'null'
        account_id:
          type:
          - string
          - 'null'
        jurisdiction:
          type:
          - string
          - 'null'
          enum:
          - default
          - eu
        bucket:
          type: string
        prefix:
          type: string
        force_path_style:
          type: boolean
        key_template:
          type: string
        environments:
          type: array
          items:
            type: string
            enum:
            - live
            - test
        public_base_url:
          type:
          - string
          - 'null'
        status:
          type: string
          enum:
          - active
          - paused
        paused_reason:
          type:
          - string
          - 'null'
          enum:
          - failures
          - workspace_deleted
          - plan
          description: '`failures`: five permanent failures in a row (test the connection again to resume); `plan`: the plan no longer includes bring your own storage.'
        access_key_hint:
          type:
          - string
          - 'null'
          example: 7QX2
          description: The last four characters of the access key id.
        last_tested_at:
          type:
          - string
          - 'null'
          format: date-time
        last_error:
          allOf:
          - $ref: '#/components/schemas/StorageError'
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Account:
      type: object
      properties:
        workspace:
          type: object
          properties:
            id:
              type: string
            slug:
              type: string
            name:
              type: string
            region:
              $ref: '#/components/schemas/Region'
        organization:
          type: object
          properties:
            id:
              type: string
            slug:
              type: string
            name:
              type: string
        plan:
          type: object
          properties:
            id:
              type: string
              example: pro
            name:
              type: string
            monthly_units:
              type: integer
        period:
          type: object
          properties:
            start:
              type: string
              format: date-time
            end:
              type: string
              format: date-time
        units:
          type: object
          properties:
            included:
              type: integer
            used:
              type: number
            overage_balance:
              type: number
        limits:
          type: object
          additionalProperties: true
    Usage:
      type: object
      properties:
        period:
          type: string
          example: 2026-09
        included:
          type: integer
        used:
          type: number
        overage_used:
          type: number
        overage_balance:
          type: number
        daily:
          type: array
          items:
            type: object
            properties:
              date:
                type: string
                format: date
              units:
                type: number
              renders:
                type: integer
        by_template:
          type: array
          items:
            type: object
            properties:
              template:
                type: string
              units:
                type: number
    StorageError:
      type: object
      description: 'The provider''s answer, reduced: its code, the HTTP status and the first line of its message.'
      properties:
        code:
          type: string
          example: AccessDenied
        http_status:
          type:
          - integer
          - 'null'
          example: 403
        message:
          type: string
          example: Access Denied
    Problem:
      type: object
      required:
      - type
      - title
      - status
      properties:
        type:
          type: string
          format: uri
          example: https://docs.formfeed.dev/errors/template-syntax
        title:
          type: string
          example: Template syntax error
        status:
          type: integer
          example: 422
        detail:
          type: string
        code:
          type: string
          enum:
          - template_not_found
          - template_syntax_error
          - template_runtime_error
          - data_validation_error
          - navigation_failed
          - render_timeout
          - page_limit_exceeded
          - output_too_large
          - file_too_large
          - source_not_found
          - source_invalid
          - source_encrypted
          - tool_failed
          - file_type_unsupported
          - office_document_invalid
          - office_document_too_large
          - office_conversion_failed
          - office_unavailable
          - unsupported_in_office
          - test_output_unavailable
          - quota_exceeded
          - rate_limited
          - region_unavailable
          - worker_exhausted
          - invalid_request
          - unauthorized
          - forbidden
          - not_found
          - conflict
          - not_implemented
          - schema_breaking_change
          - channel_not_found
          - channel_name_invalid
          - channel_in_use
          - render_input_expired
          - listen_session_limit
          - listen_session_ended
          - last_workspace
          - storage_not_configured
          - storage_test_failed
        location:
          type:
          - object
          - 'null'
          properties:
            line:
              type: integer
            column:
              type: integer
            file:
              type: string
              enum:
              - template
              - css
              - head
              - header
              - footer
            part:
              type: string
              example: word/document.xml
              description: 'Office templates: the part of the file (document, header, footer, slide).'
            paragraph:
              type: integer
              description: 'Office templates: the paragraph within the part, from 1.'
        request_id:
          type: string
        errors:
          type: array
          description: '`data_validation_error` only: every place where the data does not match the schema.'
          items:
            type: object
            properties:
              path:
                type:
                - string
                - 'null'
                example: data.invoice.number
              message:
                type: string
                example: 'data.invoice.number: must be string'
        index:
          type: integer
          description: 'Batch requests only: the first item that failed.'
  responses:
    NotFound:
      description: Not found.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Problem:
      description: Error (RFC 9457 problem details).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: API key
      description: 'API key from the workspace: `ff_live_…` or `ff_test_…`. Also accepted in the `X-API-Key` header.'
externalDocs:
  description: Guides, SDKs and the CLI
  url: https://docs.formfeed.dev