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

# Place a call

> Add one contact to a voice campaign and call it as soon as the campaign allows, or at a time up to 48 hours ahead

The campaign supplies the agent, the caller ID, the calling hours and the compliance rules. Set the campaign up first, as described in [On-Demand Calls](https://docs.squawkvoice.ai/campaigns/on-demand-calls.md#before-you-start). You don't need to launch it.

> [!WARNING]
>
> **A call is only placed when `has_pewc` is `true`, with a `pewc_date`.** Without consent the request still returns `202`, but the contact shows as **Blocked** in **On-Demand activity** and is never dialed. Send `has_pewc: true` only for contacts who have given you prior express written consent.

* **A `202` is not a call.** Calling hours, consent, opt-out, Do Not Call scrubbing and frequency caps are checked when the call is due. See [Compliance Still Applies](https://docs.squawkvoice.ai/campaigns/on-demand-calls.md#compliance-still-applies).
* **Don't retry blindly.** There is no idempotency key: each request is a new attempt with its own `request_id`, even for the same phone number, so a retry places a second call.
* **Store the `request_id`.** It is how you cancel the call if you didn't keep the `contact_id`.
* **Results stay in Studio.** No endpoint returns how a call went. Outcomes are on the campaign's **On-Demand** tab and in [Call History](https://docs.squawkvoice.ai/analyze/call-history.md).


## OpenAPI

````yaml api-reference/openapi.yaml POST /api/v1/campaigns/{campaignId}/contacts/on-demand
openapi: 3.1.0
info:
  title: SquawkVoice API
  version: '1.0'
  description: >-
    Place on-demand campaign calls, and manage and publish an agent's
    guidelines, from your own systems. Every request is authenticated with an
    API key sent in the `X-API-Key` header.
servers:
  - url: https://api.squawkvoice.ai
security:
  - apiKey: []
tags:
  - name: on-demand-calls
    x-group: On-Demand Calls
    description: Place a campaign call for one contact, and cancel it before it is dialed.
  - name: agents
    x-group: Agents
    description: >-
      Read and replace an agent's guidelines, publish the agent, and see which
      version is live.
paths:
  /api/v1/campaigns/{campaignId}/contacts/on-demand:
    post:
      tags:
        - on-demand-calls
      summary: Place a call
      description: >-
        Adds one contact to a voice campaign and dials it as soon as the
        campaign's calling hours allow, or at `scheduled_at` if you send one. A
        `202` means the contact was accepted, not that a call was placed:
        consent, calling hours and the other compliance checks run when the call
        is due.
      operationId: placeOnDemandCall
      parameters:
        - $ref: '#/components/parameters/campaignId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/OnDemandCallRequest'
            examples:
              callNow:
                summary: Call now
                value:
                  phone: '+15551234567'
                  postcode: '94107'
                  has_pewc: true
                  pewc_date: '2026-01-04'
              callLater:
                summary: Call at a set time
                value:
                  phone: '+15551234567'
                  postcode: '94107'
                  has_pewc: true
                  pewc_date: '2026-01-04'
                  scheduled_at: '2026-11-03T15:30:00'
                  scheduled_at_timezone: America/Chicago
      responses:
        '202':
          description: The contact was accepted and queued for dialing.
          content:
            application/json:
              schema:
                type: object
                required:
                  - success
                  - data
                properties:
                  success:
                    type: boolean
                    const: true
                  data:
                    $ref: '#/components/schemas/OnDemandCall'
              examples:
                callNow:
                  summary: Call now
                  value:
                    success: true
                    data:
                      contact_id: 6f1c2a9e-4b7d-4e2a-9c51-0d3e8f7a1b24
                      request_id: 2b8e4f10-9a3c-4d6e-8f21-7c5a0b9d3e18
                      run_id: 9d4a7c3e-1f2b-4e8d-a6c5-3b0e2f9d8a71
                      run_status: running
                      dialing_status: pending
                      scheduled_at: null
                      retry_after: null
                      scrub_status: pending
                      is_on_demand: true
                      dispatched_scrub: true
                callLater:
                  summary: Call at a set time
                  value:
                    success: true
                    data:
                      contact_id: 6f1c2a9e-4b7d-4e2a-9c51-0d3e8f7a1b24
                      request_id: 2b8e4f10-9a3c-4d6e-8f21-7c5a0b9d3e18
                      run_id: 9d4a7c3e-1f2b-4e8d-a6c5-3b0e2f9d8a71
                      run_status: running
                      dialing_status: callback_requested
                      scheduled_at: '2026-11-03T21:30:00.000Z'
                      retry_after: null
                      scrub_status: pending
                      is_on_demand: true
                      dispatched_scrub: false
        '400':
          description: >-
            A field is invalid: `phone`, `postcode`, the consent fields or
            `custom_fields`, or a `scheduled_at` that can't be read, is in the
            past, or is more than 48 hours away.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                success: false
                error: >-
                  Invalid phone number — must be E.164 format (e.g.
                  +15551234567)
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: The campaign isn't in your account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                success: false
                error: 'Forbidden: This API key does not have access to this campaign.'
        '404':
          description: Campaign not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                success: false
                error: Campaign not found
        '422':
          description: >-
            The postcode couldn't be resolved to a timezone, no dialing rules
            cover the contact's location, or the campaign is stopped or failed
            and isn't taking on-demand calls until it is relaunched.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                success: false
                error: Postcode could not be resolved to a timezone
        '429':
          $ref: '#/components/responses/TooManyRequests'
components:
  parameters:
    campaignId:
      name: campaignId
      in: path
      required: true
      description: The campaign's ID, copied from its **On-Demand** tab in Studio.
      schema:
        type: string
        format: uuid
  schemas:
    OnDemandCallRequest:
      type: object
      required:
        - phone
        - postcode
      properties:
        phone:
          type: string
          pattern: ^\+[1-9]\d{1,14}$
          description: >-
            The number to call, in E.164 format: `+`, the country code and the
            number, with no spaces, dashes or brackets.
          examples:
            - '+15551234567'
        postcode:
          type: string
          description: >-
            The contact's postcode. It sets the contact's timezone, which
            decides the legal calling hours. A number is also accepted, but send
            a string: JSON drops a leading zero.
          examples:
            - '94107'
        country:
          type: string
          default: US
          description: >-
            ISO 3166-1 alpha-2 country code, used with `postcode` to find the
            timezone.
          examples:
            - US
        has_pewc:
          type: boolean
          default: false
          description: >-
            Whether the contact has given prior express written consent. **A
            call is only placed when this is `true`.** Without it, the request
            still returns `202`, but the contact is blocked and never dialed.
        pewc_date:
          type: string
          description: >-
            When consent was given. Required when `has_pewc` is `true`. Accepts
            `2026-01-04`, `1/4/2026`, `01/04/2026` or `Jan 4 2026`.
          examples:
            - '2026-01-04'
        is_voice_call_opted_out:
          type: boolean
          default: false
          description: >-
            Whether the contact has opted out of voice calls. When `true`, the
            request returns `202` but the contact is blocked and never dialed.
        scheduled_at:
          type: string
          description: >-
            When to call, as ISO 8601, no more than 48 hours from now. Leave it
            out to call as soon as the campaign's calling hours allow. With an
            offset or `Z` it is an exact time. Without one, it is read in
            `scheduled_at_timezone`, or in the postcode's timezone if that is
            missing too.
          examples:
            - '2026-11-03T15:30:00-06:00'
        scheduled_at_timezone:
          type: string
          description: IANA timezone name used to read a `scheduled_at` that has no offset.
          examples:
            - America/Chicago
        custom_fields:
          type: object
          additionalProperties: true
          maxProperties: 50
          description: >-
            Values for the campaign's custom fields. Every key must already be
            set up on the campaign; a campaign with no custom fields refuses
            any. Up to 50 keys and 16 KB.
          examples:
            - plan: Premium
              renewal_date: '2026-12-01'
    OnDemandCall:
      type: object
      properties:
        contact_id:
          type: string
          format: uuid
          description: >-
            The contact's ID. Use it to [cancel the
            call](/api-reference/on-demand-calls/cancel-call).
        request_id:
          type: string
          format: uuid
          description: >-
            A new ID for this request. Store it: it is the other way to [cancel
            the call](/api-reference/on-demand-calls/cancel-call-by-request-id).
        run_id:
          type: string
          format: uuid
          description: The campaign's on-demand run, created by its first on-demand call.
        run_status:
          type: string
          description: The on-demand run's status, such as `running`.
        dialing_status:
          type: string
          enum:
            - pending
            - callback_requested
          description: >-
            `pending` for a call placed now, `callback_requested` for a call at
            `scheduled_at`.
        scheduled_at:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            When the call is scheduled for, in UTC. `null` for a call placed
            now.
        retry_after:
          type:
            - string
            - 'null'
          format: date-time
          description: >-
            When the next attempt is due, if one is waiting. `null` on a new
            call.
        scrub_status:
          type: string
          description: >-
            Where the contact is in the Do Not Call check. `pending` on a new
            call.
        is_on_demand:
          type: boolean
          const: true
        dispatched_scrub:
          type: boolean
          description: Whether this request started a new Do Not Call check for the run.
    Error:
      type: object
      description: >-
        Most errors have `success: false` and a readable `error`. Some agent
        errors and rate-limit errors carry `message` instead.
      properties:
        success:
          type: boolean
          const: false
        error:
          type: string
          description: What went wrong.
        details:
          description: More detail, when there is any.
        message:
          type: string
          description: What went wrong, on errors that don't use `error`.
        data:
          type: object
          description: >-
            Extra detail on a few errors, such as the live version when there is
            nothing to publish, or the running publish on a `409`.
  responses:
    Unauthorized:
      description: >-
        The API key is missing, unknown, revoked or expired. The reply is the
        same in every case.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            success: false
            error: 'Unauthorized: Invalid credentials'
    TooManyRequests:
      description: >-
        Too many requests. Wait for the number of seconds in the `Retry-After`
        header, then retry.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
      content:
        application/json:
          schema:
            type: object
            properties:
              status:
                type: string
                const: error
              message:
                type: string
          example:
            status: error
            message: Too many requests, please try again later.
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        An API key from **Manage → API Keys** in Studio. See
        [Authentication](/api-reference/authentication).

````

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