openapi: 3.1.0 info: title: Evermuse ingestion 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 tags: - name: ingestion description: Send data to Evermuse paths: /api/v1/ingest: post: operationId: ingestRecords tags: - ingestion summary: /ingest x-mint: href: /api-reference/ingestion/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. ' security: - IngestApiKey: [] 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-24 12:34:56+00:00 _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-24 12:40:00+00:00 _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-24 12:34:56+00:00 _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-24 15:00:00+00:00 _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-24 10:00:00+00:00 _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. /api/v1/ingest/batches: get: operationId: listBatches tags: - ingestion summary: /ingest/batches x-mint: href: /api-reference/ingestion/get-batches description: 'Returns a paginated list of batches you''ve submitted, newest first. Use the `nextCursor` value from the response as the `cursor` query parameter to fetch the next page. ' security: - IngestApiKey: [] parameters: - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 100 default: 20 description: 'Number of batches to return per page. Defaults to 20, maximum 100. ' - name: cursor in: query required: false schema: type: string maxLength: 256 description: 'Pagination cursor from a previous response''s `nextCursor` field. Omit to start from the most recent batch. ' responses: '200': description: A page of batches. content: application/json: schema: $ref: '#/components/schemas/IngestBatchListResponse' '401': description: Unauthorized. The `x-api-key` header is missing or invalid. '403': description: Forbidden. The API key does not have the required permission. '500': description: Internal server error. Please retry the request. /api/v1/ingest/batches/{batchId}: get: operationId: getBatchStatus tags: - ingestion summary: /ingest/batches/{batchId} x-mint: href: /api-reference/ingestion/get-batch-status description: 'Returns the current status and record counts for a batch you submitted. Use this to check whether your records have finished processing. ### Batch lifecycle | Status | Meaning | |---|---| | `LANDING` | Your data is being saved | | `LANDED` | Your data is saved and waiting to be processed | | `PROCESSING` | Your records are being analyzed | | `COMPLETE` | All records have been processed | | `FAILED` | Something went wrong (check errors for details) | ' security: - IngestApiKey: [] parameters: - name: batchId in: path required: true schema: type: string maxLength: 128 description: The batch ID returned when you sent your records. responses: '200': description: Batch status retrieved successfully. content: application/json: schema: $ref: '#/components/schemas/IngestBatchStatus' '401': description: Unauthorized. The `x-api-key` header is missing or invalid. '403': description: Forbidden. The API key does not have the required permission. '404': description: Batch not found. The batch ID does not exist or belongs to a different workspace. '500': description: Internal server error. Please retry the request. components: schemas: IngestBatchListResponse: type: object additionalProperties: false required: - batches - nextCursor properties: batches: type: array description: List of batches, ordered by creation time (newest first). items: $ref: '#/components/schemas/IngestBatchStatus' nextCursor: type: string nullable: true maxLength: 256 description: 'Pass this value as the `cursor` query parameter to fetch the next page. `null` when there are no more results. ' 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. ' 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' 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 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 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' IngestBatchStatus: type: object additionalProperties: false required: - batchId - status - acceptedCount - rejectedCount - totalErrorCount - createdAt properties: batchId: type: string maxLength: 128 description: Unique identifier for this batch. status: type: string enum: - LANDING - LANDED - PROCESSING - COMPLETE - FAILED description: 'Current processing status of the batch. See the batch lifecycle table in the endpoint description. ' createdAt: type: number description: When the batch was created, as Unix epoch milliseconds. example: 1709282096000 acceptedCount: type: integer minimum: 0 description: Number of records that passed validation. rejectedCount: type: integer minimum: 0 description: Number of records that failed validation. totalErrorCount: type: integer minimum: 0 description: Total number of validation errors across rejected records. errors: type: array description: Up to 20 validation errors for quick inspection. maxItems: 20 items: $ref: '#/components/schemas/IngestRecordError' source: type: string description: Where the records in this batch came from. type: type: string description: The record type for this batch. attachmentDownloadTotal: type: integer minimum: 0 description: Total number of attachments to be downloaded (shown when the batch includes file attachments). attachmentDownloadComplete: type: integer minimum: 0 description: Number of attachments successfully downloaded. attachmentDownloadFailed: type: integer minimum: 0 description: Number of attachments that failed to download. attachmentDownloadStatus: type: string enum: - PENDING - IN_PROGRESS - COMPLETE - PARTIAL_FAILURE - FAILED description: 'Overall download status for the batch''s attachments. Only shown when the batch includes file attachments. ' 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. 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 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_...` '