> ## Documentation Index
> Fetch the complete documentation index at: https://developer.folk.app/llms.txt
> Use this file to discover all available pages before exploring further.

# (Beta) Create a campaign

> Create a draft campaign: one or more steps (the first email, then its follow-ups), each a template with `{{variables}}` sent to every recipient. A draft opens in the app like one created there, to be reviewed and launched, or edited with `PATCH /v1/campaigns/:campaignId`.

Variables: For people: `{{fullName}}`, `{{firstName}}`, `{{lastName}}`, `{{jobTitle}}`, `{{description}}`, `{{urls}}`, `{{companies.name}}`, `{{companies.urls}}`. For companies: `{{name}}`, `{{description}}`, `{{fundingRaised}}`, `{{lastFundingDate}}`, `{{foundationYear}}`, `{{industry}}`, `{{employeeRange}}`. Array fields use their first value. A text or numeric custom field of the campaign's group is `{{customFieldValues.grp_<uuid>.<field name>}}`. A variable without a value for a recipient is removed. Write `\{{` for literal braces.

End a body with `<p>{{signature}}</p>` to sign it with the sender's signature; a body without it is sent unsigned.

Recipients the request names but that you can't access, and recipients without an email address, are listed in `warnings`. Only `target: "draft"` is supported for now.<br/><Warning>This endpoint is currently in open beta. The API surface may change as we iterate based on feedback.</Warning>



## OpenAPI

````yaml /schemas/2025-06-09.json post /v1/campaigns
openapi: 3.1.0
info:
  title: Folk External API
  description: >-
    Folk's public REST API lets you manage workspaces, groups, contacts, and
    real-time triggers.
  version: '2025-06-09'
  contact:
    name: folk
    email: tech@folk.app
    url: https://folk.app
servers:
  - url: https://api.folk.app
    description: Folk's public API production base URL.
    x-internal: false
security: []
tags:
  - name: Campaigns
    description: >-
      Operations related to campaigns: emails, or sequences of emails, sent to
      people or companies, and their messages.
  - name: Companies
    description: Operations related to companies.
  - name: Credits
    description: Operations related to credits.
  - name: Custom objects
    description: >-
      Operations related to objects of any type in a group, such as deals. Every
      object type shares these endpoints, with its name given as the
      `objectType` path parameter.
  - name: Groups
    description: Operations related to groups.
  - name: Group members
    description: Operations related to group members.
  - name: Group custom fields
    description: Operations related to group custom fields.
  - name: Interactions
    description: Operations related to interactions.
  - name: Notes
    description: Operations related to notes.
  - name: People
    description: Operations related to people.
  - name: Person enrichments
    description: Operations related to person enrichments.
  - name: Reminders
    description: Operations related to reminders.
  - name: Tasks
    description: Operations related to tasks.
  - name: Users
    description: Operations related to users.
  - name: Views
    description: Operations related to views.
  - name: Webhooks
    description: Operations related to webhooks.
paths:
  /v1/campaigns:
    post:
      tags:
        - Campaigns
      summary: Create a campaign
      description: >-
        Create a draft campaign: one or more steps (the first email, then its
        follow-ups), each a template with `{{variables}}` sent to every
        recipient. A draft opens in the app like one created there, to be
        reviewed and launched, or edited with `PATCH /v1/campaigns/:campaignId`.


        Variables: For people: `{{fullName}}`, `{{firstName}}`, `{{lastName}}`,
        `{{jobTitle}}`, `{{description}}`, `{{urls}}`, `{{companies.name}}`,
        `{{companies.urls}}`. For companies: `{{name}}`, `{{description}}`,
        `{{fundingRaised}}`, `{{lastFundingDate}}`, `{{foundationYear}}`,
        `{{industry}}`, `{{employeeRange}}`. Array fields use their first value.
        A text or numeric custom field of the campaign's group is
        `{{customFieldValues.grp_<uuid>.<field name>}}`. A variable without a
        value for a recipient is removed. Write `\{{` for literal braces.


        End a body with `<p>{{signature}}</p>` to sign it with the sender's
        signature; a body without it is sent unsigned.


        Recipients the request names but that you can't access, and recipients
        without an email address, are listed in `warnings`. Only `target:
        "draft"` is supported for now.<br/><Warning>This endpoint is currently
        in open beta. The API surface may change as we iterate based on
        feedback.</Warning>
      operationId: createCampaign
      parameters:
        - schema:
            type: string
            maxLength: 255
            description: >-
              A unique key, such as a UUID, that makes retrying this request
              safe: a retry with the same key and body returns the original
              response without running the operation again. Keys are kept for 24
              hours after the request completes.
            example: 8e03978e-40d5-43e8-bc93-6894a57f9324
          required: false
          name: Idempotency-Key
          in: header
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                target:
                  type: string
                  enum:
                    - draft
                    - review
                    - launch
                  description: >-
                    What to do once the campaign is created. `draft` stores it,
                    to be finished in the app or edited with `PATCH
                    /v1/campaigns/:campaignId`. `review` and `launch` are not
                    supported yet.
                  example: draft
                groupId:
                  type: string
                  minLength: 40
                  maxLength: 40
                  description: >-
                    The group to create the campaign in. Its text and numeric
                    custom fields can be used as variables. Leave it out to send
                    to a list of contacts.
                  example: grp_bc984b3f-0386-434d-82d7-a91eb6badd71
                sender:
                  $ref: '#/components/schemas/CampaignSenderInput'
                stopOnReply:
                  type: boolean
                  description: >-
                    Whether the remaining steps stop for a recipient who
                    replied. Defaults to true.
                  example: true
                scheduledAt:
                  type: string
                  maxLength: 64
                  format: date-time
                  description: When to send, with `launch`. Not supported yet.
                recipients:
                  oneOf:
                    - type: object
                      properties:
                        type:
                          type: string
                          enum:
                            - contacts
                        ids:
                          type: array
                          items:
                            type: string
                            maxLength: 64
                          minItems: 1
                          maxItems: 5000
                          description: >-
                            The people (`per_…`) or companies (`com_…`) to send
                            to, all of the same type. Merged contacts resolve to
                            the contact they were merged into. With `groupId`,
                            they must belong to the group.
                          example:
                            - per_55175e81-9a52-4ac3-930e-82792c23499b
                      required:
                        - type
                        - ids
                      additionalProperties: false
                      description: An explicit list of recipients.
                    - type: object
                      properties:
                        type:
                          type: string
                          enum:
                            - group
                        entityType:
                          type: string
                          enum:
                            - person
                            - company
                          description: Whether the recipients are people or companies.
                          example: person
                        filter:
                          type: object
                          additionalProperties:
                            type: object
                            properties:
                              empty: {}
                              not_empty: {}
                              eq: {}
                              not_eq: {}
                              like: {}
                              not_like: {}
                              gt: {}
                              lt: {}
                              all: {}
                              in: {}
                              not_in: {}
                          default: {}
                          description: >-
                            Only send to the contacts of the group matching this
                            filter, written as for `GET /v1/people` and `GET
                            /v1/companies`.
                        combinator:
                          type: string
                          enum:
                            - and
                            - or
                          default: and
                          description: >-
                            How the conditions of `filter` combine: `and`
                            (default) or `or`.
                        search:
                          type: string
                          maxLength: 1000
                          description: >-
                            Only send to the contacts of the group matching this
                            search.
                          example: Acme
                        excludeIds:
                          type: array
                          items:
                            type: string
                            maxLength: 64
                          maxItems: 5000
                          description: Contacts of the group not to send to.
                          example:
                            - per_55175e81-9a52-4ac3-930e-82792c23499b
                      required:
                        - type
                        - entityType
                      additionalProperties: false
                      description: >-
                        The people or companies of the campaign's group,
                        optionally filtered. Requires `groupId`.
                  description: >-
                    Who receives the campaign: a list of contacts, or the
                    contacts of its group. Between 1 and 5,000 recipients, all
                    people or all companies.
                steps:
                  type: array
                  items:
                    $ref: '#/components/schemas/CampaignStepInput'
                  minItems: 1
                  maxItems: 100
                  description: >-
                    The steps of the campaign: the first email, then its
                    follow-ups. More than one step needs sequences on your plan,
                    and a sender that supports them (not Gmail).
                messages:
                  type: array
                  items:
                    $ref: '#/components/schemas/CampaignPersonalizedMessageInput'
                  maxItems: 10000
                  description: >-
                    Personalized messages, with `review` and `launch`. Not
                    supported yet.
              required:
                - target
                - recipients
                - steps
              additionalProperties: false
              example:
                target: draft
                groupId: grp_bc984b3f-0386-434d-82d7-a91eb6badd71
                recipients:
                  type: contacts
                  ids:
                    - per_55175e81-9a52-4ac3-930e-82792c23499b
                steps:
                  - subject: Quick question, {{firstName}}
                    body: <p>Hi {{firstName}},</p><p>{{signature}}</p>
                  - body: >-
                      <p>Following up on my last email,
                      {{firstName}}.</p><p>{{signature}}</p>
                    delayDays: 3
                    sameThread: true
      responses:
        '201':
          description: The campaign created, with its steps.
          links:
            getCampaign:
              operationId: getCampaign
              parameters:
                campaignId: $response.body#/data/id
              description: >-
                This campaign id can be used as an input to the `GET
                /v1/campaigns/:campaignId` operation to retrieve the campaign.
            updateCampaign:
              operationId: updateCampaign
              parameters:
                campaignId: $response.body#/data/id
              description: >-
                The id returned by the `POST /v1/campaigns` operation can be
                used as an input to the `PATCH /v1/campaigns/:campaignId`
                operation to edit the draft.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
            X-RateLimit-Limit-Burst:
              $ref: '#/components/headers/X-RateLimit-Limit-Burst'
            X-RateLimit-Remaining-Burst:
              $ref: '#/components/headers/X-RateLimit-Remaining-Burst'
            X-RateLimit-Reset-Burst:
              $ref: '#/components/headers/X-RateLimit-Reset-Burst'
            RateLimit-Policy:
              $ref: '#/components/headers/RateLimit-Policy'
            RateLimit:
              $ref: '#/components/headers/RateLimit'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/CMP_Campaign'
                  deprecations:
                    type: array
                    items:
                      type: string
                    example:
                      - This field is deprecated
                required:
                  - data
              example:
                data:
                  id: cmp_9b2f1c1e-2b8a-4d1e-8f5a-3c2d1e0f9a8b
                  name: Q3 outreach
                  status: draft
                  groupId: grp_bc984b3f-0386-434d-82d7-a91eb6badd71
                  entityType: person
                  steps:
                    - position: 1
                      sender:
                        id: snd_4c3b2a19-8f7e-4d6c-9b5a-1e2f3d4c5b6a
                        email: john.doe@example.com
                        displayName: John Doe
                      subject: Quick question, {{firstName}}
                      body: <p>Hi {{firstName}},</p><p>{{signature}}</p>
                      cc: []
                      bcc: []
                      delayDays: 0
                      sameThread: false
                      status: draft
                      messageCount: 0
                      sentCount: 0
                      notSentCount: 0
                    - position: 2
                      sender:
                        id: snd_4c3b2a19-8f7e-4d6c-9b5a-1e2f3d4c5b6a
                        email: john.doe@example.com
                        displayName: John Doe
                      subject: ''
                      body: >-
                        <p>Following up on my last email,
                        {{firstName}}.</p><p>{{signature}}</p>
                      cc: []
                      bcc: []
                      delayDays: 3
                      sameThread: true
                      status: draft
                      messageCount: 0
                      sentCount: 0
                      notSentCount: 0
                  stopOnReply: true
                  recipientCount: 12
                  createdBy: usr_bc984b3f-0386-434d-82d7-a91eb6badd71
                  createdAt: '2025-07-17T09:00:00.000Z'
                  updatedAt: '2025-07-17T09:00:00.000Z'
                  scheduledAt: null
                  startedAt: null
                  finishedAt: null
                  error: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
      security:
        - bearerApiKeyAuth: []
components:
  schemas:
    CampaignSenderInput:
      anyOf:
        - type: object
          properties:
            id:
              type: string
              minLength: 40
              maxLength: 40
            email:
              type: string
              maxLength: 254
            displayName:
              type:
                - string
                - 'null'
              maxLength: 255
          required:
            - id
          additionalProperties: false
        - type: object
          properties:
            id:
              type: 'null'
            email:
              type: string
              maxLength: 254
              format: email
            displayName:
              type:
                - string
                - 'null'
              maxLength: 255
          required:
            - email
          additionalProperties: false
      description: >-
        The sender of every step. Set either a root `sender` for every step, or
        a `sender` on every step, not both. Without any, every step is sent from
        the sender you last used.
      example:
        email: john.doe@example.com
    CampaignStepInput:
      type: object
      properties:
        sender:
          allOf:
            - $ref: '#/components/schemas/CampaignSenderInput'
            - description: >-
                The sender of this step. Set either a root `sender` for every
                step, or a `sender` on every step, not both.
        subject:
          type: string
          maxLength: 1000
          description: >-
            The subject template, in plain text with `{{variables}}`. A
            follow-up sent in the same thread has no subject of its own.
          example: Quick question, {{firstName}}
        body:
          type: string
          maxLength: 200000
          description: >-
            The body template, in HTML (or plain text with `bodyFormat:
            "text"`), with `{{variables}}`. End it with `<p>{{signature}}</p>`
            to sign it with the sender's signature, and don't write the
            signature yourself: a body without `{{signature}}` is sent unsigned.
            An unsubscribe link, for SendGrid senders only, is `<a
            href="{{unsubscribeLink}}">`. HTML outside the email editor's format
            (styles, tables, unknown tags) is simplified, with warnings.
          example: <p>Hi {{firstName}},</p><p>{{signature}}</p>
        bodyFormat:
          type: string
          enum:
            - html
            - text
          description: >-
            How `body` is written. `html` (default) is read as HTML. `text` is
            plain text: a blank line starts a new paragraph, each further blank
            line adds an empty paragraph, a single newline is a line break, and
            `<` and `&` are literal characters. Bodies always read back as HTML.
          example: html
        cc:
          type: array
          items:
            type: string
            maxLength: 254
          maxItems: 50
          description: >-
            Email addresses copied on every message. At most 5 distinct
            addresses; duplicates are removed.
          example:
            - jane.doe@example.com
        bcc:
          type: array
          items:
            type: string
            maxLength: 254
          maxItems: 50
          description: >-
            Email addresses blind-copied on every message. At most 5 distinct
            addresses; duplicates are removed.
          example:
            - jane.doe@example.com
        delayDays:
          type: integer
          minimum: 0
          maximum: 365
          description: >-
            Follow-ups only: days to wait after the previous step, from 1 to
            365. Defaults to 1. The first step only takes 0, as read.
          example: 3
        sameThread:
          type: boolean
          description: >-
            Follow-ups only: send the step as a reply in the first step's
            thread, with its subject. Defaults to false.
          example: false
        position:
          description: 'Read-only: ignored.'
        status:
          description: 'Read-only: ignored.'
        messageCount:
          description: 'Read-only: ignored.'
        sentCount:
          description: 'Read-only: ignored.'
        notSentCount:
          description: 'Read-only: ignored.'
      additionalProperties: false
      description: >-
        A step of the campaign: the email template sent to every recipient, with
        the `{{variables}}` filled in for each of them.
    CampaignPersonalizedMessageInput:
      type: object
      properties:
        recipient:
          type: object
          properties:
            id:
              type: string
              maxLength: 64
          required:
            - id
          description: The person or company the message is for.
        step:
          type: integer
          minimum: 1
          maximum: 100
          description: The position of the step the message belongs to.
        subject:
          type: string
          maxLength: 1000
        body:
          type: string
          maxLength: 200000
        bodyFormat:
          type: string
          enum:
            - html
            - text
        cc:
          type: array
          items:
            type: string
            maxLength: 254
          maxItems: 50
          description: >-
            Email addresses copied on the message. At most 5 distinct addresses;
            duplicates are removed.
          example:
            - jane.doe@example.com
        bcc:
          type: array
          items:
            type: string
            maxLength: 254
          maxItems: 50
          description: >-
            Email addresses blind-copied on the message. At most 5 distinct
            addresses; duplicates are removed.
          example:
            - jane.doe@example.com
      required:
        - recipient
        - step
        - body
      description: >-
        The final content of one step for one recipient, without variables. Not
        supported yet.
    CMP_Campaign:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
          description: The name of the campaign.
          example: Q3 outreach
        status:
          type: string
          enum:
            - draft
            - processing
            - in_review
            - scheduled
            - sending
            - finished
            - cancelled
            - failed
          description: >-
            The status of the campaign. One of: draft, processing, in_review,
            scheduled, sending, finished, cancelled, failed. `processing` means
            messages are being generated: poll until `in_review`.
          example: draft
        groupId:
          type:
            - string
            - 'null'
          description: >-
            The group the campaign was created from, or null for a campaign
            created from a selection of contacts, or whose group was deleted
            since.
          example: grp_bc984b3f-0386-434d-82d7-a91eb6badd71
        entityType:
          type: string
          enum:
            - person
            - company
          description: Whether the recipients are people or companies.
          example: person
        steps:
          type: array
          items:
            $ref: '#/components/schemas/CampaignStep'
          description: >-
            The steps of the campaign, by position. A campaign that sends a
            single email has one step.
        stopOnReply:
          type: boolean
          description: Whether the remaining steps stop for a recipient who replied.
          example: true
        recipientCount:
          type: integer
          description: The number of recipients of the campaign.
          example: 12
        createdBy:
          type: string
          description: The ID of the user who created the campaign.
          example: usr_bc984b3f-0386-434d-82d7-a91eb6badd71
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type:
            - string
            - 'null'
          format: date-time
        scheduledAt:
          type:
            - string
            - 'null'
          format: date-time
        startedAt:
          type:
            - string
            - 'null'
          format: date-time
        finishedAt:
          type:
            - string
            - 'null'
          format: date-time
        error:
          type:
            - string
            - 'null'
          enum:
            - spam_detected
            - not_enough_credits
            - send_failed
            - null
          description: >-
            Why the campaign failed, when its status is `failed`. One of:
            spam_detected, not_enough_credits, send_failed.
          example: null
      required:
        - id
        - name
        - status
        - groupId
        - entityType
        - steps
        - stopOnReply
        - recipientCount
        - createdBy
        - createdAt
        - updatedAt
        - scheduledAt
        - startedAt
        - finishedAt
        - error
      description: >-
        A campaign: one or more emails, each a step, sent to a list of people or
        companies.
      example:
        id: cmp_9b2f1c1e-2b8a-4d1e-8f5a-3c2d1e0f9a8b
        name: Q3 outreach
        status: draft
        groupId: grp_bc984b3f-0386-434d-82d7-a91eb6badd71
        entityType: person
        steps:
          - position: 1
            sender:
              id: snd_4c3b2a19-8f7e-4d6c-9b5a-1e2f3d4c5b6a
              email: john.doe@example.com
              displayName: John Doe
            subject: Quick question, {{firstName}}
            body: <p>Hi {{firstName}},</p><p>{{signature}}</p>
            cc: []
            bcc: []
            delayDays: 0
            sameThread: false
            status: draft
            messageCount: 0
            sentCount: 0
            notSentCount: 0
          - position: 2
            sender:
              id: snd_4c3b2a19-8f7e-4d6c-9b5a-1e2f3d4c5b6a
              email: john.doe@example.com
              displayName: John Doe
            subject: ''
            body: >-
              <p>Following up on my last email,
              {{firstName}}.</p><p>{{signature}}</p>
            cc: []
            bcc: []
            delayDays: 3
            sameThread: true
            status: draft
            messageCount: 0
            sentCount: 0
            notSentCount: 0
        stopOnReply: true
        recipientCount: 12
        createdBy: usr_bc984b3f-0386-434d-82d7-a91eb6badd71
        createdAt: '2025-07-17T09:00:00.000Z'
        updatedAt: '2025-07-17T09:00:00.000Z'
        scheduledAt: null
        startedAt: null
        finishedAt: null
        error: null
    CampaignStep:
      type: object
      properties:
        position:
          type: integer
          minimum: 1
          description: >-
            The 1-based position of the step in the campaign. Steps are
            addressed by position.
          example: 1
        sender:
          type:
            - object
            - 'null'
          properties:
            id:
              type:
                - string
                - 'null'
              description: >-
                The ID of the sender, or null once it was deleted. `email` and
                `displayName` are then those the email was set up with.
              example: snd_4c3b2a19-8f7e-4d6c-9b5a-1e2f3d4c5b6a
            email:
              type: string
              description: The email address the email is sent from.
              example: john.doe@example.com
            displayName:
              type:
                - string
                - 'null'
              description: The name the email is sent under.
              example: John Doe
          required:
            - id
            - email
            - displayName
          description: >-
            Who the step is sent from. Each step has its own sender. `id` is
            null once the sender was deleted; `email` and `displayName` are then
            those the step was set up with. Null when the step has no sender.
        subject:
          type: string
          description: >-
            The subject template, in plain text with `{{variables}}`. Literal
            `{{` is written `\{{`.
          example: Quick question, {{firstName}}
        body:
          type: string
          description: >-
            The body template, in HTML with `{{variables}}`. Always HTML,
            whatever format it was written in. A signed body ends with
            `<p>{{signature}}</p>`; an unsubscribe link is `<a
            href="{{unsubscribeLink}}">`.
          example: <p>Hi {{firstName}},</p><p>{{signature}}</p>
        cc:
          type: array
          items:
            type: string
          description: Email addresses copied on every message.
          example: []
        bcc:
          type: array
          items:
            type: string
          description: Email addresses blind-copied on every message.
          example: []
        delayDays:
          type: integer
          minimum: 0
          description: >-
            Days to wait after the previous step before sending this one. 0 for
            the first step.
          example: 0
        sameThread:
          type: boolean
          description: >-
            Whether the step is sent as a reply in the first step's thread, with
            its subject.
          example: false
        status:
          type: string
          enum:
            - draft
            - processing
            - in_review
            - scheduled
            - sending
            - sent
            - cancelled
            - failed
          description: >-
            The status of the step. One of: draft, processing, in_review,
            scheduled, sending, sent, cancelled, failed. `scheduled` means the
            campaign is scheduled or sending, and the step waits for its send
            date.
          example: draft
        messageCount:
          type: integer
          description: The number of messages generated for this step.
          example: 0
        sentCount:
          type: integer
          description: The number of messages of this step that were sent.
          example: 0
        notSentCount:
          type: integer
          description: The number of messages of this step that were not sent.
          example: 0
      required:
        - position
        - sender
        - subject
        - body
        - cc
        - bcc
        - delayDays
        - sameThread
        - status
        - messageCount
        - sentCount
        - notSentCount
      description: >-
        A step of a campaign: the email template, with `{{variables}}`, sent to
        every recipient after the delay that follows the previous step. Each
        recipient gets their own message of the step, with the variables filled
        in.
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              example: TOO_MANY_REQUESTS
            message:
              type: string
              example: You have exceeded your rate limit.
            documentationUrl:
              type: string
              format: uri
              example: https://developer.folk.app/api-reference/errors#rate-limiting
            requestId:
              type: string
              format: uuid
              example: 123e4567-e89b-12d3-a456-426614174000
            timestamp:
              type: string
              format: date-time
              example: '2025-10-01T12:00:00Z'
            details:
              type: object
              additionalProperties: true
              example:
                policy: burst
                limit: 20
                remaining: 0
                windowSeconds: 2
                retryAfter: '2025-10-01T12:00:02.000Z'
          required:
            - code
            - message
            - documentationUrl
            - requestId
            - timestamp
      required:
        - error
      description: Error response containing error details.
    RateLimitError:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              example: TOO_MANY_REQUESTS
            message:
              type: string
              example: You have exceeded your rate limit.
            documentationUrl:
              type: string
              format: uri
              example: https://developer.folk.app/api-reference/errors#rate-limiting
            requestId:
              type: string
              format: uuid
              example: 123e4567-e89b-12d3-a456-426614174000
            timestamp:
              type: string
              format: date-time
              example: '2025-10-01T12:00:00Z'
            details:
              type: object
              properties:
                policy:
                  type: string
                  enum:
                    - default
                    - burst
                  description: The name of the rate limit policy.
                limit:
                  type: integer
                  description: The maximum number of requests allowed per window.
                remaining:
                  type: integer
                  description: The number of requests remaining in the current window.
                windowSeconds:
                  type: integer
                  description: The length of the window, in seconds.
                retryAfter:
                  type: string
                  format: date-time
                  description: When the current window resets.
                policies:
                  type: array
                  items:
                    type: object
                    properties:
                      policy:
                        type: string
                        enum:
                          - default
                          - burst
                        description: The name of the rate limit policy.
                      limit:
                        type: integer
                        description: The maximum number of requests allowed per window.
                      remaining:
                        type: integer
                        description: >-
                          The number of requests remaining in the current
                          window.
                      windowSeconds:
                        type: integer
                        description: The length of the window, in seconds.
                      retryAfter:
                        type: string
                        format: date-time
                        description: When the current window resets.
                    required:
                      - policy
                      - limit
                      - remaining
                      - windowSeconds
                      - retryAfter
                  description: >-
                    The status of every rate limit policy applied to the
                    request.
              required:
                - policy
                - limit
                - remaining
                - windowSeconds
                - retryAfter
                - policies
              description: >-
                The exceeded rate limit policy. When several are exceeded, the
                one resetting last.
          required:
            - code
            - message
            - documentationUrl
            - requestId
            - timestamp
            - details
      required:
        - error
      description: Error response returned when a rate limit is exceeded.
  headers:
    X-RateLimit-Limit:
      schema:
        type: integer
        example: 1000
      description: >-
        The maximum number of requests that you can make in the current rate
        limit window.
    X-RateLimit-Remaining:
      schema:
        type: integer
        example: 998
      description: The number of requests remaining in the current rate limit window.
    X-RateLimit-Reset:
      schema:
        type: integer
        example: 1747322958
      description: >-
        The time at which the current rate limit window resets, in UTC epoch
        seconds.
    X-RateLimit-Limit-Burst:
      schema:
        type: integer
        example: 20
      description: >-
        The maximum number of requests that you can make in the current burst
        rate limit window.
    X-RateLimit-Remaining-Burst:
      schema:
        type: integer
        example: 18
      description: The number of requests remaining in the current burst rate limit window.
    X-RateLimit-Reset-Burst:
      schema:
        type: integer
        example: 1747322902
      description: >-
        The time at which the current burst rate limit window resets, in UTC
        epoch seconds.
    RateLimit-Policy:
      schema:
        type: string
        example: '"default";q=600;w=60, "burst";q=20;w=2'
      description: >-
        The rate limit policies applied to the request, as defined by the IETF
        RateLimit header fields draft. `q` is the number of requests allowed per
        window and `w` the window length in seconds.
    RateLimit:
      schema:
        type: string
        example: '"default";r=598;t=42, "burst";r=18;t=1'
      description: >-
        The current usage of each rate limit policy, as defined by the IETF
        RateLimit header fields draft. `r` is the number of requests remaining
        and `t` the number of seconds until the window resets.
    Retry-After:
      schema:
        type: integer
        example: 60
      description: >-
        The number of seconds to wait before making a new request after hitting
        the rate limit.
  responses:
    BadRequest:
      description: The request was unacceptable, often due to missing an invalid parameter.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
        X-RateLimit-Limit-Burst:
          $ref: '#/components/headers/X-RateLimit-Limit-Burst'
        X-RateLimit-Remaining-Burst:
          $ref: '#/components/headers/X-RateLimit-Remaining-Burst'
        X-RateLimit-Reset-Burst:
          $ref: '#/components/headers/X-RateLimit-Reset-Burst'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimit-Policy'
        RateLimit:
          $ref: '#/components/headers/RateLimit'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: INVALID_REQUEST
              message: The request was invalid.
              documentationUrl: https://developer.folk.app/api-reference/errors#bad-request
              requestId: 123e4567-e89b-12d3-a456-426614174000
              timestamp: '2025-10-01T12:00:00Z'
    Unauthorized:
      description: No valid API key provided.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
        X-RateLimit-Limit-Burst:
          $ref: '#/components/headers/X-RateLimit-Limit-Burst'
        X-RateLimit-Remaining-Burst:
          $ref: '#/components/headers/X-RateLimit-Remaining-Burst'
        X-RateLimit-Reset-Burst:
          $ref: '#/components/headers/X-RateLimit-Reset-Burst'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimit-Policy'
        RateLimit:
          $ref: '#/components/headers/RateLimit'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: UNAUTHORIZED
              message: No valid API key provided.
              documentationUrl: https://developer.folk.app/api-reference/errors#unauthorized
              requestId: 123e4567-e89b-12d3-a456-426614174000
              timestamp: '2025-10-01T12:00:00Z'
    PaymentRequired:
      description: The workspace doesn’t have enough credits to perform the request.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
        X-RateLimit-Limit-Burst:
          $ref: '#/components/headers/X-RateLimit-Limit-Burst'
        X-RateLimit-Remaining-Burst:
          $ref: '#/components/headers/X-RateLimit-Remaining-Burst'
        X-RateLimit-Reset-Burst:
          $ref: '#/components/headers/X-RateLimit-Reset-Burst'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimit-Policy'
        RateLimit:
          $ref: '#/components/headers/RateLimit'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: INSUFFICIENT_CREDITS
              message: 'Not enough enrichment credits: 12 needed, 5 left.'
              documentationUrl: https://developer.folk.app/api-reference/errors#payment-required
              requestId: 123e4567-e89b-12d3-a456-426614174000
              timestamp: '2025-10-01T12:00:00Z'
    Forbidden:
      description: The API key doesn’t have permissions to perform the request.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
        X-RateLimit-Limit-Burst:
          $ref: '#/components/headers/X-RateLimit-Limit-Burst'
        X-RateLimit-Remaining-Burst:
          $ref: '#/components/headers/X-RateLimit-Remaining-Burst'
        X-RateLimit-Reset-Burst:
          $ref: '#/components/headers/X-RateLimit-Reset-Burst'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimit-Policy'
        RateLimit:
          $ref: '#/components/headers/RateLimit'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: FORBIDDEN
              message: The API key doesn’t have permissions to perform the request.
              documentationUrl: https://developer.folk.app/api-reference/errors#forbidden
              requestId: 123e4567-e89b-12d3-a456-426614174000
              timestamp: '2025-10-01T12:00:00Z'
    NotFound:
      description: The requested resource doesn’t exist.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
        X-RateLimit-Limit-Burst:
          $ref: '#/components/headers/X-RateLimit-Limit-Burst'
        X-RateLimit-Remaining-Burst:
          $ref: '#/components/headers/X-RateLimit-Remaining-Burst'
        X-RateLimit-Reset-Burst:
          $ref: '#/components/headers/X-RateLimit-Reset-Burst'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimit-Policy'
        RateLimit:
          $ref: '#/components/headers/RateLimit'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: RESOURCE_NOT_FOUND
              message: The requested resource was not found.
              documentationUrl: https://developer.folk.app/api-reference/errors#not-found
              requestId: 123e4567-e89b-12d3-a456-426614174000
              timestamp: '2025-10-01T12:00:00Z'
    Conflict:
      description: A request with the same `Idempotency-Key` is still being processed.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
        X-RateLimit-Limit-Burst:
          $ref: '#/components/headers/X-RateLimit-Limit-Burst'
        X-RateLimit-Remaining-Burst:
          $ref: '#/components/headers/X-RateLimit-Remaining-Burst'
        X-RateLimit-Reset-Burst:
          $ref: '#/components/headers/X-RateLimit-Reset-Burst'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimit-Policy'
        RateLimit:
          $ref: '#/components/headers/RateLimit'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: IDEMPOTENCY_REQUEST_IN_PROGRESS
              message: >-
                A request with the same Idempotency-Key is still being
                processed. Retry once it has completed.
              documentationUrl: https://developer.folk.app/api-reference/idempotency#errors
              requestId: 123e4567-e89b-12d3-a456-426614174000
              timestamp: '2025-10-01T12:00:00Z'
    UnprocessableEntity:
      description: >-
        The request was unacceptable, often due to missing or invalid
        parameters.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
        X-RateLimit-Limit-Burst:
          $ref: '#/components/headers/X-RateLimit-Limit-Burst'
        X-RateLimit-Remaining-Burst:
          $ref: '#/components/headers/X-RateLimit-Remaining-Burst'
        X-RateLimit-Reset-Burst:
          $ref: '#/components/headers/X-RateLimit-Reset-Burst'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimit-Policy'
        RateLimit:
          $ref: '#/components/headers/RateLimit'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: UNPROCESSABLE_ENTITY
              message: Invalid query parameters
              documentationUrl: >-
                https://developer.folk.app/api-reference/errors#unprocessable-entity
              details:
                issues:
                  - code: too_small
                    minimum: 1
                    type: number
                    inclusive: true
                    exact: false
                    message: Number must be greater than or equal to 1
                    path:
                      - limit
              requestId: 123e4567-e89b-12d3-a456-426614174000
              timestamp: '2025-10-01T12:00:00Z'
    TooManyRequests:
      description: >-
        Too many requests hit the API too quickly. We recommend an exponential
        backoff of your requests.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
        X-RateLimit-Limit-Burst:
          $ref: '#/components/headers/X-RateLimit-Limit-Burst'
        X-RateLimit-Remaining-Burst:
          $ref: '#/components/headers/X-RateLimit-Remaining-Burst'
        X-RateLimit-Reset-Burst:
          $ref: '#/components/headers/X-RateLimit-Reset-Burst'
        RateLimit-Policy:
          $ref: '#/components/headers/RateLimit-Policy'
        RateLimit:
          $ref: '#/components/headers/RateLimit'
        Retry-After:
          $ref: '#/components/headers/Retry-After'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/RateLimitError'
          example:
            error:
              code: TOO_MANY_REQUESTS
              message: You have exceeded your rate limit.
              documentationUrl: https://developer.folk.app/api-reference/errors#rate-limiting
              requestId: 123e4567-e89b-12d3-a456-426614174000
              timestamp: '2025-10-01T12:00:00Z'
              details:
                policy: burst
                limit: 20
                remaining: 0
                windowSeconds: 2
                retryAfter: '2025-10-01T12:00:02.000Z'
                policies:
                  - policy: default
                    limit: 600
                    remaining: 412
                    windowSeconds: 60
                    retryAfter: '2025-10-01T12:01:00.000Z'
                  - policy: burst
                    limit: 20
                    remaining: 0
                    windowSeconds: 2
                    retryAfter: '2025-10-01T12:00:02.000Z'
    InternalServerError:
      description: Something went wrong on our end.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: INTERNAL_SERVER_ERROR
              message: An internal server error occurred.
              documentationUrl: >-
                https://developer.folk.app/api-reference/errors#internal-server-error
              requestId: 123e4567-e89b-12d3-a456-426614174000
              timestamp: '2025-10-01T12:00:00Z'
    ServiceUnavailable:
      description: The server is overloaded or down for maintenance.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: SERVICE_UNAVAILABLE
              message: The service is currently unavailable.
              documentationUrl: >-
                https://developer.folk.app/api-reference/errors#service-unavailable
              requestId: 123e4567-e89b-12d3-a456-426614174000
              timestamp: '2025-10-01T12:00:00Z'
  securitySchemes:
    bearerApiKeyAuth:
      type: http
      scheme: bearer
      description: API key for authentication

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.