Events
The two event types, and the payload envelope they share.
load.created
Fired once, when a load record is first created. Not on update, not on status change — those are out of scope for this version.
webhook.ping
A no-op you can send at any time to test the connection. We verify the signature, store it, and acknowledge it without any processing.
It exists because testing with a real load.created is awkward for
both of us: a real load id triggers a real fetch and ingest on our
side, and a fake one fails as a lookup error that looks like a bug.
The ping gives you signing and connectivity validation on demand, in
production, with no side effects. Send it with "data": {}.
The envelope
{
"eventId": "9f2c1a84-3d7e-4b19-a0f5-1c2e6b8d4a30",
"eventType": "load.created",
"occurredAt": "2026-09-23T14:31:02Z",
"apiVersion": 1,
"data": { "loadId": 2129858 }
}| Field | Type | Notes |
|---|---|---|
eventId | UUID | Unique per event, and stable across retries — this is our idempotency key. |
eventType | string | load.created or webhook.ping. |
occurredAt | ISO 8601 | When the event happened, not when the request was sent. Must carry a zone. |
apiVersion | integer | 1. Lets either side add fields later without ambiguity. |
data.loadId | integer | For load.created: the same identifier getLoadById accepts. |
occurredAt must carry a zone designator
2026-09-23T14:31:02Z or 2026-09-23T10:31:02-04:00. A value with no
zone — 2026-09-23T14:31:02 — is rejected with 400.
This is not pedantry. We have already had a naive timestamp from LDI shift by a full day depending on where the interpreting code ran, and the resulting wrong date looked entirely plausible in the UI. A zone designator makes that failure impossible rather than unlikely.
Why we want the id and not the whole load
We deliberately do not want the full load record in the payload, even though it would save us a call.
getLoadById is already the single definition of a load on our side,
with a mapper hardened over a year of edge cases. A second
representation of the same entity would be a second thing to keep
correct, and any divergence between the two would be silent — the
worst kind.
Fetching by id also means your schema can evolve freely: a new column
appears in getLoadById and we pick it up with no change to this
webhook and no conversation.
Adding event types later
You can ship a new eventType, or bump apiVersion, without
coordinating a release with us. We accept anything we do not
recognise with 202, store it, and ignore it.
So the sequence is: you ship, we notice, we add handling, and nothing is rejected or lost in between.