Skip to main content
Alvys emits webhook events across the inbound tender lifecycle, so external systems can react to tenders as they arrive and change instead of polling the Tenders API. The authoritative list of valid event-type strings for your account is returned by Get event types (GET /api/p/v1/webhooks/event-types).
There is no wildcard subscription. Each event you want must be listed explicitly on the subscription, so an existing webhook does not start receiving tender.created or tender.change.created until you add them — see Update webhook.

Payload

Tender events use the standard Alvys webhook envelope, with one difference from load and trip events: the envelope version is v2 rather than v1. As on every event, etag identifies the version of the tender the payload was built from. Every tender payload carries data.tenderId and a data.tender object holding a full tender snapshot — the same shape Get tender returns. Event-specific fields sit alongside it.
The envelope timestamp is the moment the underlying business event occurred. For the three stop events it is when Alvys recorded the update, which can be later than the arrival, departure, or ETA the payload itself reports — use data.arrivedAt, data.departedAt, or data.estimatedAt when you need the operational time.

tender.created

Fires when a new inbound tender is received, before any review or acceptance. data carries tenderId and the tender snapshot.
This event also fires when a change tender cannot be matched to an existing shipment and Alvys creates a new tender for it. A data.tender.shipmentId you have already seen can therefore arrive a second time under a different tenderId — key your own records on tenderId, not on the shipment identifier.
Because tender.created fires on every inbound tender rather than only on tenders someone has acted on, it delivers at a substantially higher rate than the other tender events. Size your endpoint accordingly.

tender.change.created

Fires when a change tender is received, whether or not the tender has a linked load. data.queuedForReview tells the two cases apart, and it decides how to read the rest of the payload.
When queuedForReview is true, data.tender is the tender as it stands now, not as it would stand after the changes. Reading data.tender.rate on a queued LoadRate change gives you the current rate, not the proposed one. Accept the changes with Accept tender updates (POST /api/p/v1/tenders/{tenderId}/accept-updates) and read the tender again to see the applied values.
A change tender that restates what Alvys already has is still delivered, with changeCount: 0 and changeTypes: []. Use the zero count to skip it.

changeTypes

changeTypes lists the distinct categories of change proposed, in the order they appear on the tender. changeCount counts every queued update, so the two can disagree — a change tender can carry several updates of one category, and a category Alvys does not publish is counted without being named. Do not treat changeTypes.length as an upper bound on changeCount. The published vocabulary is:
These values are a stable contract. Treat any value outside this list as unrecognized rather than failing on it — the list can gain entries without notice, and unpublished categories are never named.

Notes

  • Delivery is at-least-once — deduplicate on X-Alvys-Event-Id. See Event Delivery & Reliability.
  • Tender events are not scoped to a subsidiary. Unlike load and trip events, a tender event reaches every webhook subscription in the tenant regardless of the subsidiary the subscription was created against. Filter on the tender snapshot if you need narrower routing.
  • Tender events do not carry a data.diff node. The only action available on a queued change is to accept it in full, so changeTypes is the granularity you can act on.
  • Events for the same tender are dispatched in order; ordering across different tenders is not guaranteed.