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

FieldRequiredDescription
Organization IDYesThe Client ID from SDK Keys on the Optimization tab. Forms part of the API URL path.
Environment slugYesThe 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}/events

The only required header is:

Content-Type: application/json

The 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 Signal anonymous_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 on identify (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 on identify for 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 eventNinetailed typeNinetailed event name
pagepage
Identifiedidentify
Product Viewedtrackproduct_viewed
Product Addedtrackproduct_added
Order Completedtrackorder_completed
Signed Uptracksigned_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

  1. Configure the integration with your Organization ID and Environment slug.
  2. Trigger a page and a track event from a test browser session.
  3. In Signal’s event debugger, confirm the outbound payload is wrapped in an events array and that anonymousId is populated on every event.
  4. In Contentful Personalization, open the profile for the test visitor and confirm the events and traits have landed.
  5. Verify any audiences whose conditions reference your sent traits or events now include the test profile.

Troubleshooting

ProblemSolution
404 ERR_CONFIG_NOT_FOUNDThe 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_DATAA required field is missing. Every event must have anonymousId; track events must have event; identify events must have userId and traits.
Profiles not updatingConfirm the same anonymousId is used client-side and server-side so events merge into one profile rather than creating duplicates.
Audiences not matchingCheck that the trait and property names you send exactly match the names used in your Ninetailed audience conditions.
Events rejected in bulkA 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.