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

# /ingest

> Submit records.

The request body accepts two formats:
- An array of record objects
- An object with a `records` key containing an array

For high-volume sends, use `application/x-ndjson` (one JSON object per line).

Attachments referenced by `vendor_url` are fetched automatically after the batch is accepted.
Download progress is available on the batch status endpoint.




## OpenAPI

````yaml /openapi/ingest-v1.yaml post /api/v1/ingest
openapi: 3.1.0
info:
  title: Evermuse API
  version: 1.0.0
  description: >
    The Evermuse API lets you send your data into Evermuse for analysis.

    Each record describes *what* happened and *when*, then you send it as JSON
    or newline-delimited JSON (NDJSON).


    ## Quick start


    1. Create an API key in **Settings > API Keys** with the `api:write`
    permission.

    2. `POST /api/v1/ingest` with your records (see examples below).

    3. Check `GET /api/v1/ingest/batches/{batchId}` to track progress.


    ## Authentication


    Every request must include an `x-api-key` header with your API key
    (`em_sk_...`).


    ## Content types


    | Content-Type | Use case |

    |---|---|

    | `application/json` | Array of records, or `{ "records": [...] }` wrapper |

    | `application/x-ndjson` | High-volume sends — one JSON object per line |


    ## Batching


    All records in a single request must share the same `_type` and
    `_product_id`.

    A batch can contain up to 1,000 records.
servers:
  - url: https://api.evermuse.com
    description: Evermuse API
security: []
tags:
  - name: ingestion
    description: Send data to Evermuse
paths:
  /api/v1/ingest:
    post:
      tags:
        - ingestion
      summary: /ingest
      description: >
        Submit records.


        The request body accepts two formats:

        - An array of record objects

        - An object with a `records` key containing an array


        For high-volume sends, use `application/x-ndjson` (one JSON object per
        line).


        Attachments referenced by `vendor_url` are fetched automatically after
        the batch is accepted.

        Download progress is available on the batch status endpoint.
      operationId: ingestRecords
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            type: string
            maxLength: 256
          description: >
            Optional key to prevent duplicate submissions.

            If a request with the same key and identical body has already been
            accepted,

            you'll get back the original response without the data being
            processed again.

            Recommended when retrying failed requests.
        - name: format
          in: query
          required: false
          schema:
            type: string
            enum:
              - text
              - audio
              - video
              - file
          description: >
            Optional hint that describes the primary content format of the
            records.

            This does not affect validation but helps optimize how your data is
            processed.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IngestRequest'
            examples:
              recordBatch:
                summary: Batch via JSON array
                value:
                  - _type: conversation
                    _schema_version: 1.0.0
                    _event_at: '2026-02-24T12:34:56.000Z'
                    _nature: evidence
                    _vendor_ids:
                      conversation_id: conv-001
                    _product_id: prod-abc
                    _project_ids:
                      - proj-123
                    data:
                      messages:
                        - sender: agent
                          text: How can I help you today?
                        - sender: customer
                          text: I have a question about billing.
                  - _type: conversation
                    _schema_version: 1.0.0
                    _event_at: '2026-02-24T12:40:00.000Z'
                    _nature: evidence
                    _vendor_ids:
                      conversation_id: conv-002
                    _product_id: prod-abc
                    _project_ids:
                      - proj-123
                    data:
                      messages:
                        - sender: customer
                          text: Can I upgrade my plan?
              recordsWrapper:
                summary: Batch via records wrapper
                value:
                  records:
                    - _type: email
                      _schema_version: 1.0.0
                      _event_at: '2026-02-24T12:34:56.000Z'
                      _nature: evidence
                      _vendor_ids:
                        message_id: msg-20260224-001
                      _thread_id: thread-acme-inquiry
                      _participants:
                        - id: alice@acme.com
                          name: Alice Johnson
                          email: alice@acme.com
                          role: external
                          type: contact
                        - id: bob@yourcompany.com
                          name: Bob Smith
                          email: bob@yourcompany.com
                          role: internal
                          type: user
                      _product_id: prod-abc
                      _project_ids:
                        - proj-123
                      _tags:
                        - product-feedback
                        - enterprise
                      data:
                        subject: 'Re: Product feedback'
                        from: alice@acme.com
                        to: bob@yourcompany.com
                        body: >-
                          We'd love to see dark mode support in the next
                          release.
              recordWithAttachment:
                summary: Record with a downloadable attachment
                value:
                  - _type: meeting_notes
                    _schema_version: 1.0.0
                    _event_at: '2026-02-24T15:00:00.000Z'
                    _nature: evidence
                    _product_id: prod-abc
                    _project_ids:
                      - proj-123
                    _vendor_ids:
                      meeting_id: mtg-456
                    _attachments:
                      - type: audio
                        filename: meeting-recording.mp3
                        mime_type: audio/mpeg
                        vendor_url: https://storage.example.com/recordings/mtg-456.mp3
                    data:
                      title: Weekly product sync
                      duration_minutes: 45
              guidanceRecord:
                summary: Internal guidance document
                value:
                  - _type: meeting_notes
                    _schema_version: 1.0.0
                    _event_at: '2026-02-24T10:00:00.000Z'
                    _nature: guidance
                    _vendor_ids:
                      doc_id: okr-q2-2026
                    _product_id: prod-abc
                    _project_ids:
                      - proj-456
                    data:
                      title: Q2 2026 OKRs
                      body: 'Objective: Improve onboarding conversion by 20%'
          application/x-ndjson:
            schema:
              type: string
              description: |
                Newline-delimited JSON. Each line is a single record object.
            examples:
              ndjsonExample:
                summary: NDJSON (two records)
                value: >
                  {"_type":"email","_schema_version":"1.0.0","_event_at":"2026-02-24T12:34:56Z","_nature":"evidence","_vendor_ids":{"message_id":"msg-001"},"_product_id":"prod-abc","_project_ids":["proj-123"],"data":{"subject":"Hello","body":"World"}}

                  {"_type":"email","_schema_version":"1.0.0","_event_at":"2026-02-24T12:40:00Z","_nature":"evidence","_vendor_ids":{"message_id":"msg-002"},"_product_id":"prod-abc","_project_ids":["proj-123"],"data":{"subject":"Follow-up","body":"Thanks"}}
      responses:
        '202':
          description: >
            Accepted. Your records have been received and are being processed.

            Use the returned `batchId` with `GET
            /api/v1/ingest/batches/{batchId}` to check progress.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IngestAcceptedResponse'
        '400':
          description: >
            Bad Request. Something was wrong with the data you sent.

            Check the `errors` array for details on which records were rejected
            and why.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized. The `x-api-key` header is missing or invalid.
        '403':
          description: >-
            Forbidden. The API key does not have the required `api:write`
            permission.
        '413':
          description: >-
            Payload too large. The request body exceeds the maximum allowed size
            (5 MB).
        '429':
          description: >
            Rate limit exceeded. Retry after the number of seconds indicated in
            the `retryAfter` field.
        '500':
          description: Internal server error. Please retry the request.
      security:
        - IngestApiKey: []
components:
  schemas:
    IngestRequest:
      description: |
        The request body accepts two shapes:
        - An array of record objects (up to 1,000)
        - An object with a `records` key containing an array
      oneOf:
        - type: array
          minItems: 1
          maxItems: 1000
          items:
            $ref: '#/components/schemas/IngestRecord'
        - type: object
          additionalProperties: false
          required:
            - records
          properties:
            records:
              type: array
              minItems: 1
              maxItems: 1000
              items:
                $ref: '#/components/schemas/IngestRecord'
    IngestAcceptedResponse:
      type: object
      additionalProperties: false
      required:
        - batchId
        - acceptedCount
        - rejectedCount
        - totalErrorCount
      properties:
        batchId:
          type: string
          description: >
            Unique identifier for this batch. Use it with `GET
            /api/v1/ingest/batches/{batchId}` to check status.
          maxLength: 128
        acceptedCount:
          type: integer
          minimum: 0
          description: Number of records that passed validation and were accepted.
        rejectedCount:
          type: integer
          minimum: 0
          description: Number of records that failed validation and were rejected.
        totalErrorCount:
          type: integer
          minimum: 0
          description: >
            Total number of errors across all rejected records.

            May be higher than the `errors` array length since only the first 20
            are included.
        errors:
          type: array
          description: |
            Up to 20 errors. The `index` field refers to the record's
            position in the batch you sent (starting from 0).
          maxItems: 20
          items:
            $ref: '#/components/schemas/IngestRecordError'
    ErrorResponse:
      type: object
      additionalProperties: false
      required:
        - error
        - message
      properties:
        error:
          type: string
          maxLength: 64
          description: Machine-readable error code.
        message:
          type: string
          maxLength: 2048
          description: Human-readable error description.
        retryAfter:
          type: number
          description: >
            Number of seconds to wait before trying again. Included when you've
            hit the rate limit (429 response).
        details:
          description: Additional machine-readable context about the error.
          additionalProperties: true
    IngestRecord:
      type: object
      additionalProperties: false
      required:
        - _type
        - _schema_version
        - _event_at
        - _vendor_ids
        - _product_id
        - _project_ids
        - _nature
        - data
      properties:
        _type:
          type: string
          enum:
            - email_thread
            - email
            - meeting_notes
            - call
            - call_transcription
            - conversation
            - message
            - document
            - spreadsheet
          description: >
            The type of data you're sending. All records in a single batch must
            share the same type.


            - `email_thread` — A full email conversation chain (multiple
            messages grouped together).

            - `email` — A single email message.

            - `meeting_notes` — Notes, summaries, or transcripts captured during
            or after a meeting.

            - `call` — An audio or video call record (recording url will be
            provided by the caller in the attachment).

            - `call_transcription` — A verbatim transcription of a phone or
            video call.

            - `conversation` — A multi-turn conversation from chat platforms
            (e.g. Slack, Intercom, Drift, support tickets).

            - `message` — A single standalone message (e.g. SMS, or in-app
            feedback).

            - `document` — A generic document (e.g. PDF, Word doc, or other
            written material).

            - `spreadsheet` — A spreadsheet or tabular data file (e.g. CSV,
            Excel).
        _schema_version:
          type: string
          description: |
            Schema version. Use `"1.0.0"`.
          example: 1.0.0
        _event_at:
          type: string
          description: |
            When the original event occurred, as an ISO-8601 UTC timestamp.
            Milliseconds are accepted but truncated to second precision.
          pattern: ^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$
          example: '2026-02-24T12:34:56Z'
        _vendor_ids:
          type: object
          description: >
            One or more IDs from your source system. Must contain at least one
            entry.

            Used to prevent duplicates — records with the same `_type`,
            `_vendor_ids`, and `_event_at`

            are recognized as the same record and won't be processed twice.
          minProperties: 1
          additionalProperties:
            type: string
          example:
            message_id: msg-20260224-001
        _thread_id:
          type: string
          description: >
            Optional ID that groups related records into a thread

            (e.g., an email chain, a multi-message chat, or a series of related
            calls).

            Use the thread or conversation ID from your source system.
          maxLength: 512
        _participants:
          type: array
          description: >
            People involved in the event. Used for attribution and filtering in
            your Evermuse dashboard.
          maxItems: 5000
          items:
            $ref: '#/components/schemas/Participant'
        _attachments:
          type: array
          description: >
            Files associated with this record (recordings, documents, images,
            etc.).

            Provide a `vendor_url` for files hosted elsewhere — they will be
            fetched automatically.

            Alternatively, use `upload_intent_id` to reference a file you've
            already uploaded.
          maxItems: 10
          items:
            $ref: '#/components/schemas/Attachment'
        _tags:
          type: array
          description: >
            Free-form labels for categorization and filtering (e.g.,
            `"enterprise"`, `"product-feedback"`, `"q1-2026"`).
          maxItems: 200
          items:
            type: string
            maxLength: 64
        _nature:
          type: string
          enum:
            - evidence
            - guidance
            - context
          description: >
            Classifies how this record should be treated during analysis.

            - `evidence` — actual customer conversations, meeting notes, support
            tickets, etc.

            - `guidance` — internal product team conversations, OKR documents,
            company goals

            - `context` — supplementary material used for context but not as a
            source of customer evidence
        _product_id:
          type: string
          description: >
            The Evermuse product ID to route records to.

            Found in **Settings > Projects > Actions > Copy** in the Evermuse
            dashboard.

            All records in a batch must share the same `_product_id`.
          maxLength: 128
        _project_ids:
          type: array
          description: >
            One or more Evermuse project IDs to route records to.

            Found in **Settings > Projects > Actions > Copy** in the Evermuse
            dashboard.
          minItems: 1
          items:
            type: string
            maxLength: 128
        _pii:
          $ref: '#/components/schemas/Pii'
        data:
          type: object
          description: |
            Your data. Include all relevant fields from your source system.
            This is stored exactly as you send it.
          additionalProperties: true
    IngestRecordError:
      type: object
      additionalProperties: false
      required:
        - index
        - code
        - message
      properties:
        index:
          type: integer
          minimum: 0
          description: Position of the rejected record in the submitted batch (0-based).
        code:
          type: string
          maxLength: 64
          description: Machine-readable error code (e.g., `VALIDATION_ERROR`).
        message:
          type: string
          maxLength: 2048
          description: Human-readable description of what went wrong.
    Participant:
      type: object
      additionalProperties: false
      required:
        - id
        - role
      properties:
        id:
          type: string
          description: >
            A stable identifier for the participant. Use an email address, phone
            number,

            or their ID from your source system.
          maxLength: 256
        name:
          type: string
          description: Display name of the participant.
          maxLength: 256
        email:
          type: string
          format: email
          description: Email address of the participant.
          maxLength: 320
        role:
          type: string
          enum:
            - internal
            - external
            - unknown
          description: |
            Whether the participant is part of your team or an external contact.
            - `internal` — your team member (employee, agent, rep)
            - `external` — a customer, prospect, or outside party
            - `unknown` — role cannot be determined
        type:
          type: string
          enum:
            - user
            - contact
            - workspace_member
            - bot
            - system
            - unknown
          description: |
            The kind of participant.
            - `user` — a human user
            - `contact` — a customer or prospect
            - `workspace_member` — a member of your workspace
            - `bot` — an automated agent
            - `system` — a system-generated entry
            - `unknown` — type cannot be determined
    Attachment:
      type: object
      additionalProperties: false
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - audio
            - video
            - file
            - image
            - text
            - unknown
          description: |
            The kind of attachment.
            - `audio` — audio recording (e.g., call recording, voice memo)
            - `video` — video recording
            - `file` — generic file (PDF, spreadsheet, etc.)
            - `image` — image file (screenshot, photo, etc.)
            - `text` — plain text content
            - `unknown` — type cannot be determined
        filename:
          type: string
          description: Original filename of the attachment.
          maxLength: 512
        size:
          type: integer
          description: File size in bytes.
          minimum: 0
        mime_type:
          type: string
          description: MIME type of the file (e.g., `audio/mpeg`, `application/pdf`).
          maxLength: 256
        sha1:
          type: string
          description: SHA-1 hash of the file contents (for integrity verification).
          maxLength: 128
        md5:
          type: string
          description: MD5 hash of the file contents (for integrity verification).
          maxLength: 128
        vendor_url:
          type: string
          format: uri
          description: >
            URL where the file can be downloaded. Evermuse will fetch it
            automatically

            after the batch is accepted. Download progress is available on the
            batch status endpoint.
          maxLength: 2048
        upload_intent_id:
          type: string
          description: |
            Reference to a file you've already uploaded.
            Use this to attach a previously uploaded file to this record.
          maxLength: 256
    Pii:
      type: object
      additionalProperties: false
      description: >
        Optional metadata about personal information in this record.

        Providing this helps Evermuse handle your data with the right privacy
        safeguards.
      properties:
        contains_email:
          type: boolean
          description: Whether the record's data contains email addresses.
        contains_phone:
          type: boolean
          description: Whether the record's data contains phone numbers.
        sensitive_score:
          type: number
          minimum: 0
          maximum: 1
          description: >
            A score from 0 to 1 indicating the likelihood that the record
            contains sensitive information.

            0 = no sensitive content, 1 = highly sensitive.
        detected_entities:
          type: array
          description: List of personal information types detected in the record.
          items:
            $ref: '#/components/schemas/PiiEntity'
        redaction_status:
          type: string
          enum:
            - unredacted
            - redacted
            - encrypted
          description: >
            Whether personal information in this record has been redacted or
            encrypted before sending.
    PiiEntity:
      type: object
      additionalProperties: false
      required:
        - type
      properties:
        type:
          type: string
          description: >-
            The category of personal information detected (e.g., `email`,
            `phone`, `ssn`, `credit_card`).
          maxLength: 64
        value:
          type: string
          description: The detected value (omit if you prefer not to include it).
          maxLength: 1024
        confidence:
          type: number
          minimum: 0
          maximum: 1
          description: Confidence score for the detection (0 = low, 1 = high).
  securitySchemes:
    IngestApiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: |
        Your API key, sent as the `x-api-key` header.
        Create one in **Settings > API Keys** with `api:write` permission.
        Format: `em_sk_...`

````