> ## Documentation Index
> Fetch the complete documentation index at: https://docs.firetone.com.au/llms.txt
> Use this file to discover all available pages before exploring further.

# Start a campaign

> From draft to running: the dialer starts within seconds, inside the calling window. Refused while it cannot run (no message, no contacts left, a kind without its pieces). 409 wrong_status names the status it must be in; 422 not_ready says what is missing. Requires campaigns:write.



## OpenAPI

````yaml /api-reference/openapi-public.json post /campaigns/{id}/start
openapi: 3.0.3
info:
  description: >-
    The API your own systems use: place and follow calls, have an AI agent call
    someone, run campaigns and get their results, keep contacts in step with
    your CRM, and receive signed webhooks. Authenticate with an integration key
    (Authorization: Bearer ft_...), used only from the IP addresses it allows.
  title: FireTone API
  version: 0.1.0
servers:
  - description: Your platform's API host
    url: https://{host}/api/v1
    variables:
      host:
        default: api.firet.one
security:
  - bearerAuth: []
tags:
  - name: Auth
  - description: >-
      Live calls and what can be done to them: hang up, hold, transfer, park,
      merge, monitor, whisper; the Desk's own call.
    name: Calls
  - description: 'Outbound campaigns: contacts, attempts, outcomes.'
    name: Campaigns
  - description: 'Customers: who called, what is known about them, and their memory.'
    name: Contacts
  - description: What was said on an AI call, and the review of it.
    name: Conversations
  - description: >-
      Your own systems: HTTP connections an IVR calls mid-call, and webhooks for
      call events. Tenant URLs must be public https addresses.
    name: Integrations
  - name: Live
  - name: Provisioning
  - name: Reporting
  - description: Tickets raised by people, agents and the API.
    name: Tickets
  - description: Messages left for an extension or a queue.
    name: Voicemail
paths:
  /campaigns/{id}/start:
    post:
      tags:
        - Campaigns
      summary: Start a campaign
      description: >-
        From draft to running: the dialer starts within seconds, inside the
        calling window. Refused while it cannot run (no message, no contacts
        left, a kind without its pieces). 409 wrong_status names the status it
        must be in; 422 not_ready says what is missing. Requires
        campaigns:write.
      operationId: postCampaignsIdStart
      parameters:
        - $ref: '#/components/parameters/pathId'
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Campaign'
          description: The campaign
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          description: not_ready
components:
  parameters:
    pathId:
      in: path
      name: id
      required: true
      schema:
        format: uuid
        type: string
  schemas:
    Campaign:
      properties:
        abandoned:
          description: >-
            Answered with no agent free to take it. Read-only, and the figure
            max_abandon_pct governs.
          format: int64
          type: integer
        agent_kind:
          enum:
            - human
            - virtual
          type: string
        ai_assist:
          description: >-
            Stream this campaign's answered calls to an AI that writes live
            suggestions to the agent's screen. Off by default. It costs per
            minute of every answered call and streams the customer's
            conversation to the provider for the whole call, which in several
            jurisdictions is a recording and consent question rather than only a
            cost one. The AI is never heard: an assist session's audio is
            dropped at the single point all audio leaves, so it cannot speak
            into the call even if a provider ignores the text-only instruction.
            Implemented for OpenAI only; other providers refuse rather than
            speaking.
          type: boolean
        answered:
          format: int64
          type: integer
        caller_id:
          type: string
        completed_at:
          description: 'When it completed: nobody left to dial, or stopped.'
          format: date-time
          nullable: true
          type: string
        contact_count:
          type: integer
        created_at:
          format: date-time
          type: string
        dialed:
          description: Dialer output. Read-only.
          format: int64
          type: integer
        id:
          format: uuid
          type: string
        kind:
          description: >-
            voice_broadcast plays a message to whoever answers. auto_dial
            connects the answered contact to an agent.
          enum:
            - voice_broadcast
            - auto_dial
          type: string
        max_abandon_pct:
          description: >-
            Exact decimal string. A COMPLIANCE setting: abandonment caps are law
            in most jurisdictions and 3% is the common statutory figure.
          type: string
        max_attempts:
          type: integer
        max_lines:
          description: >-
            Hard ceiling on simultaneous calls. Not a tuning knob: the node
            allows 30 new sessions per second in total, shared with inbound
            traffic.
          type: integer
        message_asset_id:
          description: A recording to play. Works today.
          format: uuid
          type: string
        message_text:
          description: >-
            What the call says, read out by a machine. May carry {{field}}
            placeholders filled from that contact's own data -- the columns
            their CSV row had -- so each person hears their own details.


            Rendered to speech once into a media asset and cached on the exact
            text and voice, so a plain message is spoken once for ten thousand
            calls and a personalised one is not re-rendered when a busy contact
            is redialled. Generated at ANSWER, inside the pause the broadcast
            already takes listening for a voicemail beep, so nothing is spent on
            calls nobody picks up and the caller hears no extra delay.


            A contact whose row is missing a field the message needs is NOT
            CALLED: the call sheet records missing_field:<name>. A sentence with
            a hole in it would go out to everybody whose row was short a column,
            sound broken, and leave nothing saying which calls were affected.
          type: string
        name:
          type: string
        organisation_id:
          format: uuid
          type: string
        pacing:
          description: >-
            balanced opens one line per free agent and abandons nobody.
            aggressive dials ahead on the measured answer rate, capped by
            max_abandon_pct.
          enum:
            - balanced
            - aggressive
          type: string
        reference:
          description: Your own id for the campaign, on every result.
          maxLength: 128
          type: string
        result_callback_url:
          description: >-
            Each contact's final outcome (campaign.contact.completed) and the
            campaign's end (campaign.completed) are POSTed here, signed with the
            organisation's callback secret; needs the secret to exist (409
            no_callback_secret).
          type: string
        schedule_days:
          description: >-
            0=Sunday to 6=Saturday, matching Go's time.Weekday. NOT the ISO
            1=Monday convention.
          items:
            type: integer
          type: array
        schedule_end:
          type: string
        schedule_start:
          description: HH:MM in the campaign's timezone.
          type: string
        script:
          description: A structured brief for a virtual agent.
          type: object
        start_at:
          description: >-
            Earliest moment this campaign may dial. Null means as soon as it is
            running. A campaign whose start is in the future is 'running' and
            dialling nothing -- there is deliberately no separate status for it.
            The daily calling window still applies on top.
          format: date-time
          nullable: true
          type: string
        status:
          description: running means the dialer is working this campaign.
          enum:
            - draft
            - running
            - paused
            - completed
          type: string
        team_id:
          format: uuid
          type: string
        timezone:
          type: string
        trunk_group_id:
          format: uuid
          type: string
        tts_voice:
          type: string
        virtual_agent_profile_id:
          format: uuid
          type: string
        workflow_id:
          description: Must be an OUTBOUND workflow.
          format: uuid
          type: string
      type: object
    Error:
      properties:
        error:
          properties:
            code:
              enum:
                - invalid_request
                - invalid_credentials
                - unauthenticated
                - forbidden
                - not_found
                - conflict
                - internal
              type: string
            message:
              type: string
          required:
            - code
            - message
          type: object
      required:
        - error
      type: object
  responses:
    NotFound:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: Not found, or not visible to the caller
    Conflict:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
      description: >-
        The row is still referenced, or a unique value is taken. The message
        names what still points at it.
  securitySchemes:
    bearerAuth:
      bearerFormat: JWT or API key
      description: >-
        Every request sends `Authorization: Bearer <token>`. The token is either
        a panel session (a JWT from /auth/login, 12 hours) or an API key
        `ft_<id>_<secret>`. An API key is accepted only from an address on its
        IP allowlist (403 ip_not_allowed otherwise; 403 ip_allowlist_required
        for an old key that has none), is limited to its rate per minute (429
        rate_limited with Retry-After; X-RateLimit-Limit/Remaining/Reset on
        every response), and at most 60 call placements a minute.
      scheme: bearer
      type: http

````