openapi: 3.2.0 info: title: Audience Ingest API description: Audience event tracking and identity resolution service contact: name: Immutable API support email: support@immutable.com url: https://support.immutable.com version: 1.0.0 servers: - url: https://api.immutable.com description: Production - url: https://api.dev.immutable.com description: Development tags: - name: ingest description: Audience event ingestion endpoints x-displayName: Ingest paths: /v1/audience/messages: post: tags: - ingest summary: Ingest audience messages operationId: IngestMessages parameters: - $ref: '#/components/parameters/publishableKeyHeader' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/MessagesRequest' responses: '200': description: OK content: application/json: schema: $ref: '#/components/schemas/MessagesResponse' '400': description: 'Bad Request (400). Two distinct cases share this status: a malformed request (missing header, empty/oversized batch) returns the generic error envelope below; every message in the batch failing validation returns the same MessagesResponse shape as a 200, with accepted:0, so callers parse rejections identically regardless of status.' content: application/json: schema: oneOf: - $ref: '#/components/schemas/APIError400' - $ref: '#/components/schemas/MessagesResponse' '401': $ref: '#/components/responses/Unauthorised' '429': $ref: '#/components/responses/TooManyRequests' '500': $ref: '#/components/responses/InternalServerError' components: schemas: BasicAPIError: type: object required: - message - link - trace_id properties: message: type: string description: Error Message example: all fields must be provided link: type: string description: Link to Immutable documentation that can help resolve this error example: https://docs.immutable.com trace_id: type: string description: Trace ID of the initial request example: e47634b79a5cd6894ddc9639ec4aad26 Surface: type: string description: One of web, unity, unreal. APIError400: allOf: - $ref: '#/components/schemas/BasicAPIError' - type: object required: - code - details properties: code: type: string description: Error Code enum: - VALIDATION_ERROR example: VALIDATION_ERROR details: type: - object - 'null' description: Additional details to help resolve the error APIError401: allOf: - $ref: '#/components/schemas/BasicAPIError' - type: object required: - code - details properties: code: type: string description: Error Code enum: - UNAUTHORISED_REQUEST example: UNAUTHORISED_REQUEST details: type: - object - 'null' description: Additional details to help resolve the error APIError429: allOf: - $ref: '#/components/schemas/BasicAPIError' - type: object required: - code - details properties: code: type: string description: Error Code enum: - TOO_MANY_REQUESTS_ERROR example: TOO_MANY_REQUESTS_ERROR details: type: - object - 'null' description: Additional details to help resolve the error ConsentStatus: type: string description: One of not_set, none, anonymous, full. MessageRejection: type: object required: - messageId - errors properties: messageId: type: string description: Echoes the client-supplied messageId verbatim, even if it is not a valid UUID. errors: type: array minItems: 1 items: $ref: '#/components/schemas/RejectionError' RejectionError: type: object required: - field - code - message properties: field: type: string description: Name of the offending property, or a message-level sentinel when no single field applies. code: type: string description: Machine-readable, stable for programmatic matching. A small open set (e.g. MISSING_REQUIRED_FIELD, INVALID_ENUM, INVALID_FORMAT, INVALID_VALUE, UNSUPPORTED_TYPE, DUPLICATE_MESSAGE_ID) that may grow over time; not a closed enum so new values are additive, not breaking. example: INVALID_FORMAT message: type: string description: Human-readable, NOT contractual. Wording may change at any time. MessagesResponse: type: object required: - success - accepted - rejected - rejections properties: success: type: boolean accepted: type: integer rejected: type: integer description: 'Count of rejected messages. accepted + rejected always equals the number of messages submitted. May exceed rejections.length: messages that share a duplicated messageId are each counted here but collapse into a single rejections entry for that id.' rejections: type: array description: Always present, even when empty. One entry per distinct rejected messageId. items: $ref: '#/components/schemas/MessageRejection' MessagesRequest: type: object required: - messages properties: messages: type: array items: $ref: '#/components/schemas/Message' minItems: 1 maxItems: 100 Message: type: object properties: type: type: string description: Required. One of track, page, screen, identify, alias. messageId: type: string description: Required. Client-generated identifier for this message. Expected to be a UUID, but validated per-message rather than at the request-parsing layer so a malformed value can be reported in `rejections` instead of failing the whole batch. eventTimestamp: type: string description: Required. Expected to be an RFC3339 timestamp, but validated per-message rather than at the request-parsing layer so a malformed value can be reported in `rejections` instead of failing the whole batch. anonymousId: type: string description: Max 256 characters. deviceId: type: string description: Max 256 characters. sessionId: type: string description: Max 256 characters. context: $ref: '#/components/schemas/EventContext' eventName: type: string description: Max 256 characters. properties: type: object additionalProperties: true userId: type: string description: Max 256 characters. identityType: $ref: '#/components/schemas/IdentityType' traits: type: object additionalProperties: true fromId: type: string description: Max 256 characters. fromType: $ref: '#/components/schemas/IdentityType' toId: type: string description: Max 256 characters. toType: $ref: '#/components/schemas/IdentityType' surface: $ref: '#/components/schemas/Surface' consentLevel: $ref: '#/components/schemas/ConsentStatus' test: type: boolean description: When true, marks the event as test traffic. The flag is passed through to the CDP as-is. EventContext: type: object properties: library: type: string description: Required. Max 256 characters. libraryVersion: type: string description: Required. Max 256 characters. userAgent: type: string description: Max 512 characters. locale: type: string description: Max 256 characters. timezone: type: string description: Max 256 characters. screen: type: string description: Max 256 characters. screenDensity: type: number format: float pageUrl: type: string description: Max 2048 characters. pagePath: type: string description: Max 2048 characters. pageReferrer: type: string description: Max 2048 characters. pageTitle: type: string description: Max 256 characters. browserLanguage: type: string description: Max 256 characters. APIError500: allOf: - $ref: '#/components/schemas/BasicAPIError' - type: object required: - code - details properties: code: type: string description: Error Code enum: - INTERNAL_SERVER_ERROR example: INTERNAL_SERVER_ERROR details: type: - object - 'null' description: Additional details to help resolve the error IdentityType: type: string description: One of passport, steam, epic, google, apple, discord, email, custom. responses: Unauthorised: description: Unauthorised (401) content: application/json: schema: $ref: '#/components/schemas/APIError401' InternalServerError: description: Internal Server Error (500) content: application/json: schema: $ref: '#/components/schemas/APIError500' TooManyRequests: description: Too Many Requests (429) headers: Retry-After: description: Seconds until the rate limit resets schema: type: integer content: application/json: schema: $ref: '#/components/schemas/APIError429' parameters: publishableKeyHeader: in: header name: x-immutable-publishable-key required: true schema: type: string securitySchemes: ImmutableApiKey: type: apiKey in: header name: x-immutable-api-key