A server posts a track to api.segment.io/v1/batch. The response is 200. Downstream destinations never see the event.
Segment's HTTP API is a collector. It is not a schema checker. The track spec marks event as required. The collector still takes a call that omits it. It does not invent a name.
{
"writeKey": "seg-write-key-abc",
"batch": [
{ "type": "track", "userId": "019mr8mf4r" }
]
}
No event. Nothing for a destination to key off. The 200 is "we received bytes."
I maintain Pixellint, an open source linter for pixels and tracking payloads. The Segment pack (vendor/segment) contracts the HTTP Tracking API, single and batched, against Segment's published spec. This is the finding:
$ pixellint validate json @batch.json
rulepack: vendor/segment (vendor: segment)
error vendor.segment.body.track_requires_an_event_name
A `track` call carries no `event`. Segment documents the event
name as required, and drops the call without it.
fix: Add the event name to the call.
docs: https://segment.com/docs/connections/spec/track/
identify, page, screen, group, and alias do not need event. The rule is scoped to type: track. A batch that mixes call types only flags the track rows.
On a single call, the type lives in the URL path (/v1/track) rather than the body, so the type enum is scoped to batch and stays quiet on a lone payload. The event-name check still applies where the body is a batch of tracks.
Every call needs a person
Segment documents that every call needs userId or anonymousId. A track with properties and a timestamp and no identity is not associated with anyone. Destinations that key on user id see nothing. Destinations that key on anonymous id see nothing.
{
"event": "Item Purchased",
"properties": { "name": "Leap to Conclusions Mat", "revenue": 14.99 },
"timestamp": "2026-07-26T00:30:12.984Z",
"writeKey": "seg-write-key-abc"
}
error vendor.segment.body.call_needs_an_identifier
The call carries neither `userId` nor `anonymousId`. Segment
documents one of the two as required on every call.
fix: Send `userId` for a known user, or `anonymousId` for an
unidentified one.
writeKey in the body is optional because Segment also accepts it as HTTP basic auth. An empty writeKey field is flagged; a missing one is not, because the key may be on the wire in the header. The pack does not pretend it can see that header from a JSON fixture.
Timestamp is ISO 8601, or omit it
Segment documents timestamp as an ISO 8601 date string. An epoch number is the wrong type. For events happening now, omit the field and let Segment stamp the call.
"timestamp": 1770000000
error vendor.segment.body.timestamp.invalid
`timestamp` is `1770000000`, which does not match
`^[0-9]{4}-[0-9]{2}-[0-9]{2}[T ][0-9]{2}:[0-9]{2}:[0-9]{2}`.
Segment documents the timestamp as an ISO 8601 date string.
Leave it out for events happening now.
fix: Send an ISO 8601 timestamp, or omit it and let Segment stamp
the call.
PostHog capture uses the same ISO 8601 shape, and an epoch value there is read as ingestion time rather than the time you sent. Amplitude wants milliseconds. There is no default timestamp type in this layer of the stack.
A batch also requires type on each row, and the value has to be one of identify, track, page, screen, group, or alias. An unrecognized type is an error. A missing type is an error. That contract is what makes a mixed batch lintable.
What the CLI actually prints
The pack matches api.segment.io and the regional segmentapis.com hosts on a URL, and it matches a JSON body that carries writeKey, userId, anonymousId, or batch[].type (and does not look like PostHog). You can lint a fixture with no host.
pixellint validate json @batch.json
rulepack: core
ok
rulepack: vendor/segment (vendor: segment)
error vendor.segment.body.track_requires_an_event_name ...
1 error(s), 0 warning(s), 0 info message(s) across 2 rulepack(s).
cargo install pixellint
# or: npm install pixellint
The contracts, with citations, live on the Segment HTTP Tracking API pack page. Paste a body into the browser playground if you do not want a clone. Nothing you paste leaves the browser.
The short version
A track needs an event name. Every call needs userId or anonymousId. timestamp is ISO 8601, or absent. The HTTP 200 is not a contract check.
Pixellint is independent of Segment. The rule ids above cite the HTTP API and the track spec because that is where the requirements live, not because this is an official tool.
Top comments (0)