Hail Calls API

The calls API from Hail — 3 operation(s) for calls.

GitHub OpenAPI

Operations 4

POST /v1/calls Create Call #
GET /v1/calls List Calls #
GET /v1/calls/{call_id} Get Call #
GET /v1/calls/{call_id}/recording Get Recording #

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

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

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

hail-so-calls-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Hail Calls API
  description: 'Give your AI agent a voice, a real phone number, and an inbox.


    This file is the source of truth for the Go CLI. Regenerate it after

    changing API routes — see docs/public/contributing.md.'
  version: 0.1.0
servers:
- url: https://api.hail.so
  description: Hail Cloud
tags:
- name: Calls
paths:
  /v1/calls:
    post:
      tags:
      - Calls
      summary: Create Call
      description: 'Place an outbound AI voice call.


        The call is placed asynchronously — this returns as soon as the call is

        queued, not when it completes. Poll GET /v1/calls/{call_id} or configure a

        webhook to get the final status and transcript. Requires

        recipient_consent=true on the request body; Hail does not verify lawful

        basis to contact the recipient, the caller warrants it.'
      operationId: create_call_v1_calls_post
      parameters:
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      - name: Idempotency-Key
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Idempotency-Key
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CallCreate'
      responses:
        '201':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallResponse'
        '429':
          description: Rate limited. The agent-origin workspace exceeded a per-channel velocity cap, or the platform kill switch is on. Retry after the Retry-After header (seconds). Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
            RateLimit-Limit:
              description: The request ceiling for the current window.
              schema:
                type: integer
            RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
        '402':
          description: insufficient credits; top up at https://hail.so/console/billing
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
    get:
      tags:
      - Calls
      summary: List Calls
      description: 'List calls for the caller''s organization, newest first.


        Cursor-paginated: pass the returned next_cursor to fetch the next page;

        a null next_cursor means there are no more results. Filter by status or

        destination number (to) to narrow the list.'
      operationId: list_calls_v1_calls_get
      parameters:
      - name: cursor
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Cursor
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          maximum: 200
          minimum: 1
          default: 50
          title: Limit
      - name: status
        in: query
        required: false
        schema:
          anyOf:
          - enum:
            - queued
            - dialing
            - ringing
            - in_progress
            - completed
            - failed
            - busy
            - no_answer
            - canceled
            type: string
          - type: 'null'
          title: Status
      - name: to
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: To
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallListResponse'
        '429':
          description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
            RateLimit-Limit:
              description: The request ceiling for the current window.
              schema:
                type: integer
            RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /v1/calls/{call_id}:
    get:
      tags:
      - Calls
      summary: Get Call
      description: 'Fetch one call by id, including its current status and end reason.


        Org-scoped: returns 404 for a call belonging to a different organization

        (not 403, to avoid confirming the id exists). Use this to poll for the

        final outcome of a call placed with POST /v1/calls.'
      operationId: get_call_v1_calls__call_id__get
      parameters:
      - name: call_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
          title: Call Id
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallResponse'
        '429':
          description: Rate limited. This caller exceeded the general request-rate ceiling. Retry after the Retry-After header (seconds).
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
            RateLimit-Limit:
              description: The request ceiling for the current window.
              schema:
                type: integer
            RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            RateLimit-Reset:
              description: Seconds until the current window resets.
              schema:
                type: integer
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /v1/calls/{call_id}/recording:
    get:
      tags:
      - Calls
      summary: Get Recording
      description: Redirect to a short-lived recording URL. Returns 404 if unavailable or expired.
      operationId: get_recording_v1_calls__call_id__recording_get
      parameters:
      - name: call_id
        in: path
        required: true
        schema:
          type: string
          format: uuid
          title: Call Id
      - name: authorization
        in: header
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          title: Authorization
      responses:
        '302':
          description: Successful Response
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    CallCreate:
      properties:
        recipient_consent:
          type: boolean
          title: Recipient Consent
          description: Attestation that you have obtained the lawful consent required to contact this recipient. Hail does not verify consent itself — you are responsible for a lawful basis under TCPA/ePrivacy/PECR/CAN-SPAM/GDPR as applicable. Rejected (422) if not true.
        consent_source:
          anyOf:
          - type: string
          - type: 'null'
          title: Consent Source
          description: Where/how consent was obtained (e.g. 'signup form', 'prior customer relationship'). Required (non-empty) when message_type is 'marketing'.
        consent_obtained_at:
          anyOf:
          - type: string
            format: date-time
          - type: 'null'
          title: Consent Obtained At
          description: When consent was obtained, if known.
        message_type:
          type: string
          enum:
          - marketing
          - informational
          title: Message Type
          description: '''marketing'' additionally requires a non-empty consent_source. Use ''informational'' for transactional/service communications.'
          default: informational
        to:
          type: string
          title: To
          description: Recipient phone number, E.164 format (e.g. +14155551234).
        from:
          anyOf:
          - type: string
          - type: 'null'
          title: From
          description: 'Caller-id phone number, E.164 format. Must be a number owned by the organization with the voice capability. Omitted: an active org-owned number is used if one exists, else a number is claimed from the shared pool.'
        system_prompt:
          anyOf:
          - type: string
          - type: 'null'
          title: System Prompt
          description: Task instructions for the agent, sent as its leading system message. At least one of system_prompt or llm is required; both together is also valid.
        llm:
          anyOf:
          - $ref: '#/components/schemas/LLMConfig'
          - type: 'null'
          description: BYO LLM endpoint the call runs on instead of Hail's default model. At least one of system_prompt or llm is required; both together is also valid.
        first_message:
          anyOf:
          - type: string
          - type: 'null'
          title: First Message
          description: 'Opening line the agent speaks first. Omitted: the agent waits for the callee to speak first.'
        recording_enabled:
          anyOf:
          - type: boolean
          - type: 'null'
          title: Recording Enabled
          description: Best-effort audio recording for this call. true records and false does not, whatever the workspace setting. Null inherits the agent, then the workspace. Recording failure never prevents the call. The recording-enabled call rate applies regardless of capture outcome.
        ai_disclosure:
          type: boolean
          title: Ai Disclosure
          description: Speak the AI self-disclosure line ('Hi, this is an AI assistant calling on behalf of ...') as the first thing on the call. Enabled by default. Disable only if you have verified the disclosure is not required for this call — 47 CFR 64.1200(b)(1) requires identifying the initiating business at the start of artificial-voice calls in the US, and several jurisdictions have AI bot-disclosure laws. Hail does not verify this for you. The agent still identifies itself as an AI if asked.
          default: true
        voice_config:
          $ref: '#/components/schemas/VoiceConfig'
          description: TTS voice, VAD, turn-detection, and spoken-language settings for this call.
        metadata:
          additionalProperties: true
          type: object
          title: Metadata
          description: Free-form JSON object attached to the call and echoed back on reads. Not interpreted by Hail. Keys starting with 'recording_' and the key 'failure_code' are reserved and rejected.
        tools:
          anyOf:
          - items:
              type: string
            type: array
          - type: 'null'
          title: Tools
          description: 'Agent tools to allow on this call. Omitted: every tool the organization''s configured channels support (new channels appear automatically). Empty list: no tools. Tool names are validated against the server''s registry.'
        agent_id:
          anyOf:
          - type: string
            format: uuid
          - type: 'null'
          title: Agent Id
          description: Place the call with a saved agent (POST /agents). The agent supplies system_prompt, first_message, ai_disclosure, recording_enabled, voice_config, tools and max duration; any of those given explicitly on this request wins. Satisfies the system_prompt or llm requirement on its own. A paused agent is refused with 409.
      additionalProperties: false
      type: object
      required:
      - recipient_consent
      - to
      title: CallCreate
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
          description: 'List of validation errors: each entry gives the field location (loc), the problem (msg), and the error type (type).'
      type: object
      title: HTTPValidationError
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
            - type: string
            - type: integer
          type: array
          title: Location
          description: Path to the invalid field within the request, as a list of keys/indices (e.g. ['body', 'to']).
        msg:
          type: string
          title: Message
          description: Human-readable description of the validation failure.
        type:
          type: string
          title: Error Type
          description: Machine-readable error type code (e.g. 'missing', 'string_type').
        input:
          title: Input
          description: The value that was actually provided and failed validation.
        ctx:
          type: object
          title: Context
          description: Additional machine-readable context for the error, when the error type provides one.
      type: object
      required:
      - loc
      - msg
      - type
      title: ValidationError
    CallResponse:
      properties:
        id:
          type: string
          format: uuid
          title: Id
          description: Unique identifier for this call.
        organization_id:
          type: string
          format: uuid
          title: Organization Id
          description: Organization that placed or received this call.
        from_e164:
          type: string
          title: From E164
          description: Caller-id phone number used, E.164 format.
        to_e164:
          type: string
          title: To E164
          description: Recipient phone number, E.164 format.
        direction:
          type: string
          enum:
          - outbound
          - inbound
          title: Direction
          description: '''outbound'' for calls Hail placed, ''inbound'' for calls received.'
        agent_id:
          anyOf:
          - type: string
            format: uuid
          - type: 'null'
          title: Agent Id
          description: Agent that answered (inbound) or placed (outbound) this call. Null when the call was placed with an inline system_prompt.
        status:
          type: string
          enum:
          - queued
          - dialing
          - ringing
          - in_progress
          - completed
          - failed
          - busy
          - no_answer
          - canceled
          title: Status
          description: 'Current call-progress state: ''queued'', ''dialing'', ''ringing'', ''in_progress'', or one of the terminal states ''completed'', ''failed'', ''busy'', ''no_answer'', ''canceled''.'
        end_reason:
          anyOf:
          - type: string
          - type: 'null'
          title: End Reason
          description: Machine-readable reason the call reached a terminal status (e.g. 'normal_hangup', 'user_rejected', 'sip_trunk_failure'). Null while the call is still in progress.
        provider_call_sid:
          anyOf:
          - type: string
          - type: 'null'
          title: Provider Call Sid
          description: The telephony provider's identifier for this call leg, if assigned.
        livekit_room:
          anyOf:
          - type: string
          - type: 'null'
          title: Livekit Room
          description: Name of the LiveKit room hosting this call's media session, if one was created.
        initial_prompt:
          anyOf:
          - type: string
          - type: 'null'
          title: Initial Prompt
          description: The system_prompt this call was created with, if any.
        recording_enabled:
          type: boolean
          title: Recording Enabled
          description: Whether the recording-enabled call rate applies, regardless of capture outcome.
          default: false
        recording_status:
          type: string
          title: Recording Status
          description: disabled, requested, starting, recording, processing, ready, unavailable, failed, or erased.
          default: disabled
        recording_duration_ms:
          anyOf:
          - type: integer
          - type: 'null'
          title: Recording Duration Ms
          description: Duration of captured audio, independent of billed call duration.
        recording_s3_key:
          anyOf:
          - type: string
          - type: 'null'
          title: Recording S3 Key
          description: Internal storage key for the call recording. Not a directly fetchable URL.
        requested_at:
          type: string
          format: date-time
          title: Requested At
          description: When the call was requested, ISO 8601 timestamp.
        started_at:
          anyOf:
          - type: string
            format: date-time
          - type: 'null'
          title: Started At
          description: When dialing began, ISO 8601 timestamp. Null until the call starts.
        answered_at:
          anyOf:
          - type: string
            format: date-time
          - type: 'null'
          title: Answered At
          description: When the callee answered, ISO 8601 timestamp. Null if never answered.
        ended_at:
          anyOf:
          - type: string
            format: date-time
          - type: 'null'
          title: Ended At
          description: When the call ended, ISO 8601 timestamp. Null while still in progress.
        failure_message:
          anyOf:
          - type: string
          - type: 'null'
          title: Failure Message
          description: Human-readable explanation for a failed call. Null for other statuses.
      type: object
      required:
      - id
      - organization_id
      - from_e164
      - to_e164
      - direction
      - status
      - end_reason
      - provider_call_sid
      - livekit_room
      - initial_prompt
      - recording_s3_key
      - requested_at
      - started_at
      - answered_at
      - ended_at
      title: CallResponse
    CallListResponse:
      properties:
        items:
          items:
            $ref: '#/components/schemas/CallResponse'
          type: array
          title: Items
          description: Calls in this page, newest first.
        next_cursor:
          anyOf:
          - type: string
          - type: 'null'
          title: Next Cursor
          description: Opaque cursor for the next page. Null when there are no more results.
      type: object
      required:
      - items
      title: CallListResponse
    VoiceConfig:
      properties:
        tts:
          type: string
          const: cartesia
          title: Tts
          description: Text-to-speech provider. Currently only 'cartesia'.
          default: cartesia
        vad:
          type: string
          const: silero
          title: Vad
          description: Voice-activity-detection engine. Currently only 'silero'.
          default: silero
        turn_detection:
          type: string
          const: livekit
          title: Turn Detection
          description: Turn-detection engine. Currently only 'livekit'.
          default: livekit
        voice_id:
          anyOf:
          - type: string
          - type: 'null'
          title: Voice Id
          description: 'Per-call TTS voice override, applied to whichever TTS provider serves the call. Omitted: the organization''s or environment''s default voice.'
        language:
          anyOf:
          - type: string
            enum:
            - ar
            - bg
            - bn
            - cs
            - da
            - de
            - el
            - en
            - es
            - fi
            - fr
            - gu
            - he
            - hi
            - hr
            - hu
            - id
            - it
            - ja
            - kn
            - ko
            - mr
            - ms
            - nl
            - 'no'
            - pl
            - pt
            - ro
            - ru
            - sk
            - sv
            - ta
            - te
            - th
            - tl
            - tr
            - uk
            - vi
            - zh
          - type: 'null'
          title: Language
          description: 'Spoken language for the call as a lowercase ISO 639-1 code (e.g. ''da''). One of the 39 supported codes — see docs/languages.md. Applied to STT, TTS, and turn detection. Omitted: the providers'' defaults (English).'
      additionalProperties: false
      type: object
      title: VoiceConfig
    LLMConfig:
      properties:
        base_url:
          type: string
          title: Base Url
          description: Public HTTPS base URL of the BYO LLM endpoint the call runs on. Must resolve to a public address — private/internal hosts are rejected.
        api_key:
          type: string
          title: Api Key
          description: API key sent to the BYO LLM endpoint. Write-only — never echoed back.
        model:
          type: string
          title: Model
          description: Model name to request from the BYO LLM endpoint.
      additionalProperties: false
      type: object
      required:
      - base_url
      - api_key
      - model
      title: LLMConfig