HTTP Endpoints
Signal exposes four browser-facing endpoints consumed by the Datafly.js SDK. All four accept POST requests with a JSON body and are authenticated via a pipeline key.
Endpoints
| Endpoint | Method | Description |
|---|---|---|
/v1/t | POST | Track a custom event |
/v1/p | POST | Track a page view |
/v1/i | POST | Identify a user |
/v1/g | POST | Associate a user with a group |
Authentication
Browser requests authenticate using a pipeline key (dk_...). The key can be provided in two ways:
- Authorization header (preferred for programmatic use):
Authorization: Bearer dk_live_abc123...data-pipeline-keyattribute on the Datafly.js script tag (the SDK reads this and includes it automatically):
<script
src="https://data.example.com/datafly.js"
data-pipeline-key="dk_live_abc123..."
></script>Signal validates the pipeline key on every request. If the key is missing, invalid, or revoked, Signal returns a 401 Unauthorized response.
Track Event — POST /v1/t
Tracks a named event with optional properties.
Request
{
"type": "track",
"event": "Product Added",
"properties": {
"product_id": "SKU-1234",
"name": "Wireless Headphones",
"price": 79.99,
"currency": "USD",
"quantity": 1
},
"context": {
"page": {
"url": "https://shop.example.com/products/headphones",
"title": "Wireless Headphones | Shop",
"referrer": "https://shop.example.com/category/audio"
},
"userAgent": "Mozilla/5.0 ...",
"locale": "en-US",
"timezone": "America/New_York"
},
"timestamp": "2026-02-25T14:30:00.000Z",
"messageId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}Response
{
"success": true
}Status codes: 200 on success, 400 for invalid payload, 401 for invalid pipeline key, 429 if rate-limited.
Page Event — POST /v1/p
Records a page view. Datafly.js sends this automatically on every page load and client-side navigation.
Request
{
"type": "page",
"name": "Product Detail",
"properties": {
"url": "https://shop.example.com/products/headphones",
"title": "Wireless Headphones | Shop",
"referrer": "https://shop.example.com/category/audio",
"path": "/products/headphones",
"search": "?color=black"
},
"context": {
"userAgent": "Mozilla/5.0 ...",
"locale": "en-US"
},
"timestamp": "2026-02-25T14:30:00.000Z",
"messageId": "b2c3d4e5-f6a7-8901-bcde-f12345678901"
}Response
{
"success": true
}Identify Event — POST /v1/i
Associates the current anonymous visitor with a known user identity.
Request
{
"type": "identify",
"userId": "user_98765",
"traits": {
"email": "jane@example.com",
"name": "Jane Smith",
"plan": "premium",
"createdAt": "2025-03-15T10:00:00.000Z"
},
"context": {
"userAgent": "Mozilla/5.0 ...",
"locale": "en-US"
},
"timestamp": "2026-02-25T14:30:00.000Z",
"messageId": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}Response
{
"success": true
}After an identify call, Signal links the anonymous identifier to the provided userId. All subsequent events from this visitor will carry both identifiers.
Group Event — POST /v1/g
Associates a user with a group such as a company, team, or account.
Request
{
"type": "group",
"groupId": "org_555",
"traits": {
"name": "Acme Corp",
"industry": "Technology",
"employees": 250,
"plan": "enterprise"
},
"context": {
"userAgent": "Mozilla/5.0 ...",
"locale": "en-US"
},
"timestamp": "2026-02-25T14:30:00.000Z",
"messageId": "d4e5f6a7-b8c9-0123-defa-234567890123"
}Response
{
"success": true
}Common Request Fields
Every event sent to these endpoints includes the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Event type: track, page, identify, or group |
event | string | Track only | Name of the event (e.g. "Order Completed") |
name | string | Page only | Name of the page (e.g. "Checkout") |
userId | string | No | Known user ID (required for identify) |
groupId | string | Group only | Group identifier |
properties | object | No | Event-specific data |
traits | object | No | User or group attributes (identify/group events) |
context | object | No | Contextual data (page, user agent, locale, etc.) |
timestamp | string | No | ISO 8601 timestamp; defaults to server receipt time |
messageId | string | No | Client-generated UUID for deduplication |
Common Response Headers
All endpoints return the following headers:
| Header | Value |
|---|---|
Content-Type | application/json |
Access-Control-Allow-Origin | Configured origin(s) for the source |
Access-Control-Allow-Methods | POST, OPTIONS |
Access-Control-Allow-Headers | Content-Type, Authorization |
Access-Control-Allow-Credentials | true |
CORS
Signal handles OPTIONS preflight requests automatically. Allowed origins are derived from the source configuration associated with the pipeline key. If the Origin header does not match any configured origin, the request is rejected with a 403 Forbidden response.
In development mode, localhost origins are allowed by default. In production, you must explicitly configure allowed origins in the source settings via the Management UI.
Rate Limiting
Browser endpoints are rate-limited per pipeline key. The default limit is 1,000 requests per second per source. When the limit is exceeded, Signal returns a 429 Too Many Requests response with a Retry-After header.
Error Responses
All endpoints return JSON error bodies in a consistent shape:
{
"error": "invalid_pipeline_key",
"message": "The supplied pipeline key was not recognised."
}| Status | When | Retry behaviour |
|---|---|---|
200 | Event accepted | — |
400 | Malformed JSON, missing required field, or schema validation failed | Don’t retry — fix the payload |
401 | Invalid or missing pipeline key | Don’t retry — check the key |
403 | Origin not in source allowlist (browser endpoints), or IP-allowlist mismatch (server endpoints) | Don’t retry — fix configuration |
413 | Payload exceeded the request size limit (typically 1 MB) | Don’t retry — split into smaller batches |
429 | Rate limit exceeded for this pipeline key | Retry after the Retry-After header value |
500 | Unexpected server-side error | Retry with exponential backoff |
502 / 504 | Gateway is briefly unavailable (rolling deploy, upstream timeout) | Retry with exponential backoff |
503 | Backpressure — Signal is overloaded and is shedding load to protect downstream | Retry with exponential backoff; honour Retry-After if present |
For server-side integrations, we recommend retrying 5xx responses with exponential backoff starting at 1 second, doubling up to 60 seconds, with jitter. Treat any 4xx response other than 429 as a permanent failure for that event — log it and move on.
The official Datafly server-side libraries handle this retry logic for you.