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

# Upload a conversation

> Provider-agnostic conversation ingestion. POST a conversation in Zelto's canonical shape (the default), or a native Vapi/Retell webhook payload selected with the `X-Zelto-Provider` header. The delivery is acknowledged immediately and processed asynchronously into a conversation + transcript. Deduplicated by conversation id: re-uploading the same conversation updates it in place and never double-charges. Non-terminal native events (e.g. Vapi `status-update`) are accepted and ignored.



## OpenAPI

````yaml /docs-openapi.json post /webhooks/calls
openapi: 3.1.0
info:
  title: Zelto REST API
  version: v1
  description: >-
    Programmatic access to your Zelto organization. Every endpoint is scoped to
    the organization the API key belongs to. Mint a key at Settings →
    Integrations → API integration.
servers:
  - url: https://api.zelto.ai
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Agents
    description: Voice and text agents normalised through Zelto's ingestion pipeline.
  - name: Conversations
    description: >-
      Phone calls, chats, and voicemails — plus their transcripts and
      recordings.
  - name: Customer Metrics
    description: >-
      Customer-provided evaluations attached to a conversation (requires metrics
      enabled).
  - name: Reviews
    description: Human review state for conversations.
  - name: Buckets
    description: Reviewer routing — group conversations for batched review.
  - name: Companies
    description: Clients or brands your agents work for, and their logos.
  - name: Findings
    description: >-
      Structured issues discovered during analysis, with optional conversation
      attachments and comments.
  - name: Experiments
    description: >-
      Prospective measurement of two externally routed prompt versions across
      independent monitor outcomes.
  - name: Organizations
    description: >-
      Headless org provisioning. Requires an admin/service API key (not a
      read-only org-scoped customer key).
paths:
  /webhooks/calls:
    servers:
      - url: https://ingest.zelto.ai
        description: AWS ingestion endpoint
    post:
      tags:
        - Conversations
      summary: Upload a conversation
      description: >-
        Provider-agnostic conversation ingestion. POST a conversation in Zelto's
        canonical shape (the default), or a native Vapi/Retell webhook payload
        selected with the `X-Zelto-Provider` header. The delivery is
        acknowledged immediately and processed asynchronously into a
        conversation + transcript. Deduplicated by conversation id: re-uploading
        the same conversation updates it in place and never double-charges.
        Non-terminal native events (e.g. Vapi `status-update`) are accepted and
        ignored.
      operationId: uploadCall
      parameters:
        - name: X-Zelto-Provider
          in: header
          required: false
          description: >-
            Which schema the request body uses. Defaults to `zelto` (the
            canonical CallIngest shape, also used by `livekit`). Native `vapi` /
            `retell` webhook payloads are also accepted.
          schema:
            type: string
            enum:
              - zelto
              - vapi
              - retell
              - livekit
            default: zelto
      requestBody:
        required: true
        description: >-
          When `X-Zelto-Provider` is `zelto` (or omitted), the canonical
          CallIngest body. When `vapi` / `retell`, that provider's native
          webhook payload.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CallIngest'
      responses:
        '200':
          description: >-
            Accepted for processing. Also returned for duplicate deliveries and
            dropped (non-terminal) events.
          content:
            application/json:
              schema:
                type: object
                required:
                  - received
                properties:
                  received:
                    type: boolean
        '400':
          $ref: '#/components/responses/ValidationFailed'
        '401':
          $ref: '#/components/responses/Unauthorized'
components:
  schemas:
    CallIngest:
      type: object
      required:
        - call
      description: >-
        Zelto's canonical conversation upload shape (`X-Zelto-Provider: zelto`).
        Provide either `agent.externalId` (find-or-create) or `agentId` (attach
        to an existing agent).
      properties:
        agent:
          type: object
          description: >-
            Find-or-create an agent by your own id. Required unless `agentId` is
            given.
          properties:
            externalId:
              type: string
              minLength: 1
            name:
              type: string
              minLength: 1
              maxLength: 255
              description: Used only when the agent is first created.
        agentId:
          type: string
          format: uuid
          description: >-
            Attach to an existing Zelto agent. Takes precedence over
            `agent.externalId`.
        version:
          type: string
          minLength: 1
          maxLength: 255
          description: >-
            Release label of the agent that actually handled this conversation.
            Optional for ingestion; send it on every conversation for reliable
            experiments. Case-sensitive, 1–255 characters after trimming. The
            same agent and label reuse a version; a new label creates one on the
            first processed conversation. Keep the agent ID stable and change
            this label for prompt or pipeline changes. Preserve the original
            label on retries. This is a top-level field, not metadata.version.
            See /docs/agent-versions.
        companyExternalId:
          type: string
          minLength: 1
          maxLength: 255
          description: >-
            Your stable per-conversation id for the client or brand this
            conversation belongs to. When Companies is enabled, Zelto maps it to
            a company; otherwise the agent's default company is used when
            available.
        call:
          type: object
          required:
            - externalId
          properties:
            externalId:
              type: string
              minLength: 1
              description: >-
                Your conversation id. Dedup key — re-uploading the same id
                updates the conversation in place.
            startedAt:
              type: string
              format: date-time
            endedAt:
              type: string
              format: date-time
            durationSeconds:
              type: integer
              minimum: 0
              description: Derived from startedAt/endedAt when omitted.
            endedReason:
              type: string
              maxLength: 100
            cost:
              type: number
              minimum: 0
              description: Cost in dollars.
            recordingUrl:
              type: string
              maxLength: 2048
            recordingUploadId:
              type: string
              format: uuid
              description: >-
                Opaque id returned by POST /v1/recording-uploads after direct
                upload of a local recording. Mutually exclusive with
                recordingUrl.
            customer:
              type: object
              properties:
                number:
                  type: string
                  maxLength: 50
                  description: Inbound caller number; used to resolve a receiver.
                name:
                  type: string
                  maxLength: 255
        transcript:
          type: object
          required:
            - turns
          properties:
            turns:
              type: array
              minItems: 1
              items:
                $ref: '#/components/schemas/CallIngestTurn'
        systemPrompt:
          type: string
          description: >-
            Actual system prompt of the version that handled this conversation.
            A leading system transcript turn is used if omitted. Available text
            is captured when a version is first observed, or fills a missing
            prompt later; an existing prompt snapshot is not overwritten. A
            version label alone does not supply a prompt.
        metadata:
          type: object
          additionalProperties: true
          description: Free-form metadata stored on the conversation.
        versionConfig:
          allOf:
            - $ref: '#/components/schemas/AgentVersionConfig'
          description: >-
            Context for the version that handled this conversation. Requires the
            version field. Captured when the version is observed; does not
            overwrite an existing configuration snapshot. Use config when
            registering through POST /v1/agents/{id}/versions.
    CallIngestTurn:
      type: object
      required:
        - role
        - content
      properties:
        role:
          type: string
          enum:
            - user
            - assistant
            - system
            - tool
        content:
          type: string
          minLength: 1
        startSeconds:
          type: number
          minimum: 0
        endSeconds:
          type: number
          minimum: 0
        words:
          type: array
          description: >-
            Optional word-level audio timing used for synchronized transcript
            playback.
          items:
            type: object
            required:
              - word
              - startSeconds
            properties:
              word:
                type: string
                minLength: 1
              startSeconds:
                type: number
                minimum: 0
              endSeconds:
                type: number
                minimum: 0
    AgentVersionConfig:
      type: object
      additionalProperties: true
      description: >-
        Complete version context as a JSON object, up to 64 KiB serialized as
        UTF-8. Can include llm, stt, tts, tools, workflows, retrieval, and
        custom settings. Records context only; does not deploy or execute this
        configuration. Do not include secrets.
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          description: >-
            Human-readable error message, e.g. Unauthorized or Validation
            failed.
        message:
          type: string
        details:
          type: object
          additionalProperties: true
  responses:
    ValidationFailed:
      description: Request body failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        Send `Authorization: Bearer YOUR_API_KEY`. The key resolves to a single
        organization.

````

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