Appearance
Segment-compatible ingest
Aletheia takes Segment's tracking calls (identify, track, and batch of them) at /segment/v1, so the events a product already sends to Segment can feed velocity rules and automatic workflow triggers without a second integration: a Segment destination forwards them, or a service that speaks Segment's HTTP API sends them straight to Aletheia.
Sending
| Route | Body |
|---|---|
POST /segment/v1/track | One track message |
POST /segment/v1/identify | One identify message |
POST /segment/v1/batch | { "batch": [...], "sentAt"? }, up to 1 000 messages of any type |
Requests carry a service user's token (Authorization: Bearer <token>) whose role holds events:write and subjects:write, such as the integration role; a body may be up to 1 MB. In Segment, add a Webhooks (Actions) destination (or a Destination Function) that posts the events to https://<api>/segment/v1/batch with that header. Segment's write-key authentication (HTTP Basic) is not taken: a library that sends it, such as analytics-node, needs a proxy that swaps it for the token.
What a message becomes
identify: the subject whose external id is theuserId(asPOST /decideandPOST /subjectsknow it), created when the tenant has none, itstraitsmerged into the subject's data as a JSON merge patch. Creating or changing it is audited (subject.created,subject.updated) and starts thesubject.createdworkflows.track: an event for that subject, created when missing:
| Aletheia event | From the Segment message |
|---|---|
type | event, lowercased, other characters as _: Order Completed is order_completed |
idempotencyKey | segment:<messageId>: a message Segment sends again is a duplicate |
occurredAt | timestamp, corrected by how far the sender's clock was off (sentAt against the arrival) |
attributes | properties |
amount | The first of revenue, total, amount, value that holds a number (amountProperties) |
currency | properties.currency, when it is three letters |
ip, deviceId | context.ip, context.device.id |
email | An email in context.traits.email or properties.email |
page,screen,group,alias: acknowledged and ignored.- A message with an
anonymousIdand nouserIdis ignored, unless the tenant keeps anonymous visitors as subjects (anon:<anonymousId>).
The events are stored as POST /events stores them: the same checks of occurredAt (at most a day ahead, not before 2000, no older than the tenant keeps events), the same velocity keys and event keys, and the same automatic workflow triggers.
The answer
Every request answers 200 with success, so Segment does not send it again; what could not be taken up is listed with why:
json
{
"success": true,
"accepted": 41,
"duplicates": 2,
"ignored": 6,
"subjectsCreated": 3,
"subjectsUpdated": 1,
"rejected": [{ "index": 17, "messageId": "ajs-…", "reason": "messageId is required for track" }]
}A message is rejected for no userId or anonymousId, a track without messageId or with an event name that leaves nothing, a time that is not a date or that ingestion refuses, or a userId naming a subject of another kind. A body that is not Segment's (no batch list, more than 1 000 messages) answers 400, and a token without the permissions 403.
Settings
settings.segment (PUT /tenants/me/settings) sets how messages map:
| Setting | Default | What it does |
|---|---|---|
subjectKind | user | The kind of the subjects identify and track create |
anonymous | ignore | subject keeps a subject anon:<anonymousId> per visitor |
amountProperties | ["revenue", "total", "amount", "value"] | The properties the amount is read from, the first with a number |