Ninetailed (Contentful Personalization)
Datafly Signal delivers events directly to Ninetailed (now Contentful Personalization) using the Experience API server-side events endpoint. Page views, identification, and behavioural signals reach Ninetailed over a first-party server-to-server call, so visitor profiles, audiences, and experience targeting keep building even when the browser blocks client-side scripts.
The events endpoint upserts profiles: when an event arrives for a profile id that does not exist yet, Ninetailed creates it. There is no need to pre-create profiles.
Prerequisites
- A Contentful Personalization (Ninetailed) account with at least one environment.
- Your Organization ID (the Client ID shown under SDK Keys on the Optimization tab).
- The Environment slug of the environment you want to write into (for example
main). - Your audience and experience configuration set up in Ninetailed against the trait and property names you intend to send.
Configuration
| Field | Required | Description |
|---|---|---|
| Organization ID | Yes | The Client ID from SDK Keys on the Optimization tab. Forms part of the API URL path. |
| Environment slug | Yes | The target environment slug (e.g. main). Forms part of the API URL path. |
The Experience API events endpoint is scoped entirely by the Organization ID and Environment slug in the URL path — there is no separate API key or bearer token to manage for this endpoint.
Configure in Signal
Add the integration
In Signal, add the Ninetailed integration to your pipeline.
Enter your identifiers
Paste your Organization ID and Environment slug.
Choose a preset
Select the Default preset, then enable the events you send (page, identify, and track events).
Save and publish
Save the integration and publish the pipeline.
API Endpoint
POST https://experience.ninetailed.co/v2/organizations/{organization_id}/environments/{environment_slug}/eventsThe only required header is:
Content-Type: application/jsonThe request body wraps one or more events in an events array. Each event is a Segment-style object:
{
"events": [
{
"type": "track",
"event": "add_to_cart",
"anonymousId": "a1b2c3d4-...",
"channel": "web",
"properties": { "sku": "SKU-A", "price": 49.99 },
"context": { "page": { "url": "https://example.com/p/widget" } }
}
]
}A single call accepts up to 200 events, with a maximum of 50 identify events, across a maximum of 50 unique profiles. Signal batches automatically within these limits.
Identity Signals
Ninetailed builds and merges a profile from the identifiers Signal sends. Unlike advertising destinations, Ninetailed stores traits as plain profile attributes for personalisation and audience targeting, so traits are sent as-is and are never hashed.
anonymousId— the Signalanonymous_id. Sent on every event and is the primary key the events endpoint requires. Mirror the same anonymous id that the Ninetailed browser SDK uses (if present) so server-side and client-side events stitch to one profile.userId— your authenticated user id, sent onidentify(and carried on later events once known). Aliases the anonymous profile to a known user.traits— attributes such as email, first name, last name, plan, or any custom field, attached to the profile onidentifyfor use in audience conditions.
How to send user data
Call datafly.identify() when a user logs in, registers, or updates their details:
datafly.identify("user-123", {
email: "jane.doe@example.com",
firstName: "Jane",
lastName: "Doe",
plan: "pro"
});Signal forwards these as traits on an identify event, upserting the profile.
Event Mapping
Signal event names are GA4-style snake_case and map to Ninetailed track events of the same name (Ninetailed accepts arbitrary event names). Page views map to the page event type; identification maps to the identify type.
| Signal event | Ninetailed type | Ninetailed event name |
|---|---|---|
page | page | — |
Identified | identify | — |
Product Viewed | track | product_viewed |
Product Added | track | product_added |
Order Completed | track | order_completed |
Signed Up | track | signed_up |
To customise, edit the integration’s Field Mappings in the Management UI.
Example: Product Viewed event
Datafly.js call:
datafly.track("Product Viewed", {
product_id: "SKU-A",
product_name: "Widget",
category: "Tools",
price: 49.99,
currency: "USD"
});Ninetailed payload sent by Signal:
{
"events": [
{
"type": "track",
"event": "product_viewed",
"anonymousId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"userId": "user-123",
"channel": "web",
"messageId": "evt_9f8e7d6c",
"timestamp": "2026-06-06T10:15:00.000Z",
"properties": {
"product_id": "SKU-A",
"product_name": "Widget",
"category": "Tools",
"price": 49.99,
"currency": "USD"
},
"context": {
"page": {
"url": "https://example.com/p/widget",
"path": "/p/widget",
"referrer": "https://example.com/",
"search": ""
},
"locale": "en-US"
}
}
]
}Testing
- Configure the integration with your Organization ID and Environment slug.
- Trigger a
pageand atrackevent from a test browser session. - In Signal’s event debugger, confirm the outbound payload is wrapped in an
eventsarray and thatanonymousIdis populated on every event. - In Contentful Personalization, open the profile for the test visitor and confirm the events and traits have landed.
- Verify any audiences whose conditions reference your sent traits or events now include the test profile.
Troubleshooting
| Problem | Solution |
|---|---|
404 ERR_CONFIG_NOT_FOUND | The Organization ID or Environment slug in the URL path is wrong. Re-check the Client ID under SDK Keys and the environment slug. |
400 ERR_INVALID_DATA | A required field is missing. Every event must have anonymousId; track events must have event; identify events must have userId and traits. |
| Profiles not updating | Confirm the same anonymousId is used client-side and server-side so events merge into one profile rather than creating duplicates. |
| Audiences not matching | Check that the trait and property names you send exactly match the names used in your Ninetailed audience conditions. |
| Events rejected in bulk | A single call is capped at 200 events / 50 identify events / 50 unique profiles. Signal batches within these limits; very large historical backfills should be chunked. |