openapi: 3.0.1 info: title: Ninetailed Experience Events API description: The core REST API for the Ninetailed (now Contentful Personalization) platform. Returns visitor profiles and resolves which experience and variant to render based on audience rules. Deployed on the edge for high performance. Supports profile creation, updates, retrieval, deletion, event tracking, and batch event import for audience segmentation and A/B test delivery. version: '2' contact: name: Ninetailed Support url: https://www.contentful.com/help/ license: name: Proprietary servers: - url: https://experience.ninetailed.co/v2/organizations/{organizationId}/environments/{environmentSlug} description: Ninetailed Experience API edge endpoint variables: organizationId: description: The organization ID (Client ID) found in the SDK Keys section on the Optimization tab. default: YOUR_ORGANIZATION_ID environmentSlug: description: The environment slug (e.g. "main"). default: main tags: - name: Events description: Batch event ingestion endpoints for profile enrichment. paths: /events: post: operationId: batchUpsertSync summary: Batch Upsert Events (Synchronous) description: Synchronous batch endpoint for upserting events across multiple profiles. Supports up to 200 events total, a maximum of 50 identify events, and up to 50 unique profiles per request. Returns an array of profiles without experiences or changes. tags: - Events parameters: - $ref: '#/components/parameters/locale' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BatchEventsRequest' example: events: - type: identify userId: user-abc traits: plan: enterprise - type: page userId: user-abc properties: url: https://example.com/pricing path: /pricing text/plain: schema: $ref: '#/components/schemas/BatchEventsRequest' responses: '200': description: Events processed; returns array of updated profiles. content: application/json: schema: $ref: '#/components/schemas/BatchEventsResponse' '400': $ref: '#/components/responses/BadRequest' '429': $ref: '#/components/responses/TooManyRequests' '500': $ref: '#/components/responses/InternalServerError' /import/events: post: operationId: batchUpsertAsync summary: Batch Upsert Events (Asynchronous) description: Asynchronous fire-and-forget batch endpoint for importing events. Returns 200 immediately without waiting for processing. Ideal for high-volume imports where response data is not needed. tags: - Events requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BatchEventsRequest' text/plain: schema: $ref: '#/components/schemas/BatchEventsRequest' responses: '200': description: Request accepted for asynchronous processing. content: application/json: schema: $ref: '#/components/schemas/AsyncConfirmation' '400': $ref: '#/components/responses/BadRequest' '500': $ref: '#/components/responses/InternalServerError' components: parameters: locale: name: locale in: query required: false description: BCP 47 locale string used for resolving localized experiences. Defaults to "en". schema: type: string default: en example: en-US schemas: Session: type: object description: Session-level metadata for the visitor. properties: id: type: string description: Unique session identifier. isReturningVisitor: type: boolean description: True if the visitor has been seen in a previous session. landingPage: type: string description: URL of the first page visited in this session. count: type: integer description: Number of sessions this visitor has had. sessionLength: type: integer description: Length of the current session in seconds. Location: type: object description: Geographic location resolved from the visitor's IP address. properties: lat: type: number format: double description: Latitude coordinate. lon: type: number format: double description: Longitude coordinate. city: type: string description: City name. region: type: string description: Region or state code. country: type: string description: ISO 3166-1 alpha-2 country code. timezone: type: string description: IANA timezone identifier (e.g. "America/New_York"). AsyncConfirmation: type: object description: Confirmation that an asynchronous import request was accepted. properties: data: type: object description: Empty object; processing happens asynchronously. error: type: object nullable: true message: type: string example: ok BatchEventsResponse: type: object description: Response envelope for synchronous batch events, containing profile updates without experiences. properties: data: type: array description: Array of updated profiles. items: $ref: '#/components/schemas/Profile' error: type: object nullable: true message: type: string example: ok Profile: type: object description: A visitor profile containing identity, behavioral, and session data used for personalization. properties: id: type: string description: Unique identifier for the visitor profile. stableId: type: string description: Stable cross-session identifier for the visitor. random: type: number format: double minimum: 0 maximum: 1 description: Random value (0-1) used for traffic splitting in A/B tests. audiences: type: array description: List of audience IDs the visitor currently qualifies for. items: type: string traits: type: object additionalProperties: true description: Merged identify traits for the visitor. location: $ref: '#/components/schemas/Location' session: $ref: '#/components/schemas/Session' jurisdiction: type: string description: Regulatory jurisdiction code (e.g. "GDPR") resolved from location. stickyVariants: type: object additionalProperties: type: string description: Map of experience ID to variant index for sticky (consistent) assignment. BatchEventsRequest: type: object required: - events description: Request body for batch event ingestion. Maximum 200 events, 50 identify events, 50 unique profiles per request. properties: events: type: array maxItems: 200 description: Array of events to ingest across multiple profiles. items: $ref: '#/components/schemas/Event' ApiError: type: object description: Error response envelope. required: - message - error properties: message: type: string description: Human-readable error message. data: type: object description: Empty object on error. error: type: object description: Structured error details. properties: code: type: string enum: - ERR_INVALID_DATA - ERR_CONFIG_NOT_FOUND - ERR_PROFILE_NOT_FOUND - ERR_NAMESPACE_MISMATCH - ERR_PROFILE_OVERLOAD - ERR_INTERNAL_SERVER_ERROR description: Machine-readable error code. Event: type: object required: - type description: A behavioral or identity event associated with a visitor profile. properties: type: type: string enum: - page - track - identify - screen - component description: The type of event being recorded. userId: type: string description: Stable user identifier for linking anonymous and identified sessions. anonymousId: type: string description: Anonymous identifier for unidentified visitors. name: type: string description: Name of the page, screen, or track event. traits: type: object additionalProperties: true description: Key-value pairs describing user attributes (used with identify events). properties: type: object additionalProperties: true description: Key-value pairs describing event properties (used with page/track events). properties: url: type: string format: uri description: Full URL of the page. path: type: string description: Path portion of the URL. title: type: string description: Page title. referrer: type: string format: uri description: Referring URL. timestamp: type: string format: date-time description: ISO 8601 timestamp of when the event occurred. responses: BadRequest: description: Invalid request data. content: application/json: schema: $ref: '#/components/schemas/ApiError' example: message: Invalid data provided data: {} error: code: ERR_INVALID_DATA TooManyRequests: description: Profile has exceeded the allowed number of concurrent requests. content: application/json: schema: $ref: '#/components/schemas/ApiError' example: message: Profile overload data: {} error: code: ERR_PROFILE_OVERLOAD InternalServerError: description: An unexpected server error occurred. content: application/json: schema: $ref: '#/components/schemas/ApiError' example: message: Internal server error data: {} error: code: ERR_INTERNAL_SERVER_ERROR