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

# Start an individual call

> Start an audio Call in an existing individual Chat containing exactly one user and one agent. The caller comes from authentication. The user must have added the agent; sending or replying to a Message, or adding the agent, establishes that relationship. An agent calls only a person who added it and who has not turned off Allow Calls for it; Allow Calls is the person's own switch for each agent they added, on by default, and it never stops the person's own call to the agent. A block in either direction prevents the call. A group Chat is rejected even when it has only two current members. Reuse the same Idempotency-Key and body after an uncertain response. Relay emits `call.created` through the agent's configured event transport. Both Contacts use the authenticated Call room for signaling and publish one WebRTC `audio` track. The callee answers by sending `join` within the 32-second ring lease; silence ends `no-answer`. When the other Contact's Call to you in this Chat is still ringing, a create answers 200 with that Call instead of starting a second one; joining its room answers it. A Call to a person who is already ringing or in-progress on another Call is created and finished at once with status `busy`; an agent is never marked busy by Relay. A Call to an agent whose webhooks do not include an active subscription to `call.created` is created and finished at once with status `no-answer`, because that agent can never hear it ring; an agent with no webhook subscription receives call events over its WebSocket. An answered Call has no duration limit. When calls are switched off on this server, every create is refused with 503 and nothing is written; `calls_enabled` on GET /v1/me says whether calls are on. An agent's call to a person none of whose devices runs a Relay build that takes calls is refused with 422 and nothing is written (no Call, no call marker, no missed call).



## OpenAPI

````yaml /api-reference/openapi.mint.yaml post /v1/chats/{chatId}/calls
openapi: 3.1.0
info:
  title: Relay API
  version: 1.0.0
  description: >-
    Create conversations between users and agents.


    Send multipart Messages, manage Chats, upload Attachments, and receive agent
    events.
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
servers:
  - url: https://api.staging.relayapp.im
    description: Relay API
security:
  - BearerAuth: []
tags:
  - name: Agents
    x-page-title: Agents
    description: >-
      Read who owns the authenticated agent, manage who is always and never
      allowed to message it, manage its OAuth2 client for Log in with Relay, and
      delete existing developer-managed agents.
  - name: Chats
    x-page-title: Chats
    description: Create, retrieve, and update direct or group chats.
  - name: Messages
    x-page-title: Messages
    description: Send and retrieve messages, replies, reactions, and receipts.
  - name: Attachments
    x-page-title: Attachments
    description: Allocate, upload, retrieve, and delete attachment bytes.
  - name: Blocked Handles
    x-page-title: Blocked Handles
    description: |-
      Block or unblock registered Relay Handles. In a direct Chat, a block in
      either direction means a Message is stored for the sender and never
      delivered to the other party, silently. In a group Chat, blocking is one
      way: you do not receive Messages from a Handle you blocked, but that
      Handle still receives yours, and every other member receives as normal.
      Blocking does not delete existing Chats or history.
  - name: Contact Card
    x-page-title: Contact Card
    description: Manage the authenticated agent's Relay contact card.
  - name: Webhooks
    x-page-title: Webhooks
    description: Register signed webhook destinations.
  - name: WebSocket
    description: Receive agent events over a durable acknowledged WebSocket.
  - name: Contacts
    description: Request a user Contact.
  - name: Directory
    description: Find public agents by category or task. No credential is needed.
  - name: Payments
    x-page-title: Payments
    description: Ask a person to pay, on your organization's own connected Stripe account.
  - name: Calls
    description: >-
      Start, answer, end and read individual user-agent audio calls. Call events
      use Relay's normal agent event delivery, and both Contacts join the
      authenticated Call room as WebRTC audio participants.
paths:
  /v1/chats/{chatId}/calls:
    post:
      tags:
        - Calls
      summary: Start an individual call
      description: >-
        Start an audio Call in an existing individual Chat containing exactly
        one user and one agent. The caller comes from authentication. The user
        must have added the agent; sending or replying to a Message, or adding
        the agent, establishes that relationship. An agent calls only a person
        who added it and who has not turned off Allow Calls for it; Allow Calls
        is the person's own switch for each agent they added, on by default, and
        it never stops the person's own call to the agent. A block in either
        direction prevents the call. A group Chat is rejected even when it has
        only two current members. Reuse the same Idempotency-Key and body after
        an uncertain response. Relay emits `call.created` through the agent's
        configured event transport. Both Contacts use the authenticated Call
        room for signaling and publish one WebRTC `audio` track. The callee
        answers by sending `join` within the 32-second ring lease; silence ends
        `no-answer`. When the other Contact's Call to you in this Chat is still
        ringing, a create answers 200 with that Call instead of starting a
        second one; joining its room answers it. A Call to a person who is
        already ringing or in-progress on another Call is created and finished
        at once with status `busy`; an agent is never marked busy by Relay. A
        Call to an agent whose webhooks do not include an active subscription to
        `call.created` is created and finished at once with status `no-answer`,
        because that agent can never hear it ring; an agent with no webhook
        subscription receives call events over its WebSocket. An answered Call
        has no duration limit. When calls are switched off on this server, every
        create is refused with 503 and nothing is written; `calls_enabled` on
        GET /v1/me says whether calls are on. An agent's call to a person none
        of whose devices runs a Relay build that takes calls is refused with 422
        and nothing is written (no Call, no call marker, no missed call).
      operationId: createCall
      parameters:
        - name: chatId
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 255
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CallCreateRequest'
      responses:
        '200':
          description: |-
            Nothing was created. Either the original Call for the same
            idempotent request, or, when the other Contact in this Chat is
            already calling you and that Call still rings, that ringing Call
            (`from` is the other Contact). You are its callee: send `join` in
            its Call room, as you would after any create, and the join answers
            it. Two Contacts that call each other at the same moment therefore
            end up on one Call.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallResult'
        '201':
          description: Call created and ringing.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CallResult'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: |-
            The call is not permitted (error code `2003`). A block in either
            direction, a member who is not active, or a person who has not
            added the calling agent is refused with "Call permission is
            required.". When the person added the agent but turned off Allow
            Calls for it, the agent's call is refused with "This person turned
            off calls from this agent.". Tell the person in your own words;
            they can turn Allow Calls on again in the agent's profile or in
            Settings.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          description: |-
            The Call cannot be placed (error code `1005`). A Chat that is not
            an individual Chat containing exactly one user and one agent is
            refused. An agent's call to a person who has no device running a
            Relay build that takes calls is refused with "This person's Relay
            app cannot take calls yet. They need to update Relay."; no Call
            and no call marker are written, so the person sees no missed
            call. Tell the person in your own words, or send a Message
            instead.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          $ref: '#/components/responses/InternalServerError'
        '503':
          $ref: '#/components/responses/CallsUnavailable'
      security:
        - BearerAuth: []
components:
  schemas:
    CallCreateRequest:
      type: object
      additionalProperties: false
      required:
        - to
      properties:
        to:
          type: array
          minItems: 1
          maxItems: 1
          items:
            type: string
            minLength: 1
    CallResult:
      type: object
      additionalProperties: false
      required:
        - call
      properties:
        call:
          $ref: '#/components/schemas/Call'
    ErrorResponse:
      type: object
      required:
        - error
        - success
      properties:
        error:
          $ref: '#/components/schemas/ErrorDetail'
        success:
          type: boolean
          description: Always false for error responses
        trace_id:
          type: string
          description: Unique trace ID for request tracing and debugging
    Call:
      type: object
      additionalProperties: false
      required:
        - id
        - chat_id
        - from
        - to
        - status
        - revision
        - created_at
        - ringing_at
        - answered_at
        - ended_at
      properties:
        id:
          type: string
          format: uuid
        chat_id:
          type: string
          format: uuid
        from:
          $ref: '#/components/schemas/CallContact'
        to:
          type: array
          minItems: 1
          maxItems: 1
          items:
            $ref: '#/components/schemas/CallContact'
        status:
          type: string
          description: >-
            `ringing` and `in-progress` are live. `completed`, `no-answer`,
            `canceled`, `busy` and `failed` are terminal and set `ended_at`.
          enum:
            - ringing
            - in-progress
            - completed
            - no-answer
            - canceled
            - busy
            - failed
        revision:
          type: integer
          minimum: 1
        created_at:
          type: string
          format: date-time
        ringing_at:
          type: string
          format: date-time
        answered_at:
          type:
            - string
            - 'null'
          format: date-time
        ended_at:
          type:
            - string
            - 'null'
          format: date-time
    ErrorDetail:
      type: object
      required:
        - status
        - code
        - message
        - doc_url
      properties:
        status:
          type: integer
          description: HTTP status code (e.g., 400, 404, 500)
        code:
          $ref: '#/components/schemas/ErrorCode'
        message:
          type: string
          description: Human-readable error message
        doc_url:
          type: string
          description: Link to documentation for this error code
        retry_after:
          type: integer
          description: >-
            Number of seconds to wait before retrying. Only present on 429 rate
            limit errors.
    CallContact:
      type: object
      additionalProperties: false
      required:
        - id
        - handle
        - kind
      properties:
        id:
          type: string
          format: uuid
        handle:
          type: string
          minLength: 1
        kind:
          type: string
          enum:
            - user
            - agent
        timezone:
          type:
            - string
            - 'null'
          maxLength: 64
          examples:
            - America/Detroit
          description: >-
            The person's IANA time zone name, as their Relay app last reported
            it; null until the app reports one. When the person uses Relay on
            more than one device, the device they used last sets it. Present
            only when kind is user. Timestamps stay in UTC; use this to read
            them in the person's local time.
        age_range:
          type:
            - string
            - 'null'
          enum:
            - under_13
            - 13_15
            - 16_17
            - 18_plus
            - null
          examples:
            - 18_plus
          description: >-
            The person's age range, as their Relay app last reported it: from
            Apple's Declared Age Range, or from a birth year the person gave
            once (only the range is kept). Null until the app reports one.
            Present only when kind is user. A person whose range is not 18_plus
            never reaches an agent rated 18_plus, so an agent never needs to ask
            anyone their age.
        links:
          type: array
          maxItems: 5
          uniqueItems: true
          items:
            type: string
            format: uri
            maxLength: 2048
            pattern: ^https://
          examples:
            - - https://github.com/ana
              - https://www.instagram.com/ana
          description: >-
            The person's profile links, as they set them in Relay: at most 5
            absolute https URLs, in the order they chose, each stored as the
            WHATWG URL parser normalises it (scheme and host lowercased, a
            non-ASCII host as punycode) and different from the others after
            normalising. Relay refuses control characters and whitespace,
            userinfo, hosts that are not public (localhost, IP literals,
            single-label names, .local and other private names), and a host
            label that mixes Latin with another script. Empty when they set
            none. Present only when kind is user. Relay sends no platform name;
            read the site from the URL.
        about:
          type:
            - string
            - 'null'
          maxLength: 160
          examples:
            - Builds robots in Ann Arbor.
          description: >-
            The person's about, as they wrote it in Relay: plain text, at most
            160 characters, with no control characters. Null when they wrote
            none. Present only when kind is user.
        rive:
          description: >-
            The Rive file this agent shows in its calls, so the app can load it
            before the call connects. Null when it has none. Present only when
            kind is agent.
          anyOf:
            - $ref: '#/components/schemas/RiveFile'
            - type: 'null'
    ErrorCode:
      type: integer
      description: Relay API error code.
    RiveFile:
      type: object
      additionalProperties: false
      required:
        - file
        - artboard
        - state_machine
        - view_model
      description: >-
        A Rive file an agent shows in its calls: a character, a quiz, a game, a
        chart. The app draws `artboard` and runs `state_machine`, binding
        `view_model`; the agent drives it live on the call's `rive` data
        channel. Names are Rive's own; a null name means the file's default.
      properties:
        file:
          type: string
          format: uri
          description: >-
            The .riv Relay hosts, at a permanent address that never changes its
            bytes: `<cdn>/rive/<contact id>/<version>/file-<hash>.riv`.
          examples:
            - >-
              https://cdn.relayapp.im/rive/0199a000-0000-7000-8000-00000000000b/1790000000000/file-0123456789abcdef.riv
        artboard:
          type:
            - string
            - 'null'
          maxLength: 255
          examples:
            - Call
        state_machine:
          type:
            - string
            - 'null'
          maxLength: 255
          examples:
            - Main
        view_model:
          type:
            - string
            - 'null'
          maxLength: 255
          examples:
            - Main
  responses:
    BadRequest:
      description: Invalid request - validation error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Unauthorized:
      description: Unauthorized - missing or invalid authentication
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    Conflict:
      description: The request conflicts with the current state of the resource
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    CallsUnavailable:
      description: >-
        Calls are switched off on this server (error code `3006`, "Calls are not
        available on this server."). No Call was created and nothing was
        written. `calls_enabled` on GET /v1/me is false while this holds.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: >
        Bearer token authentication. Include your API token in the Authorization
        header.


        Format: `Authorization: Bearer <your-token>`

````

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