> ## Documentation Index
> Fetch the complete documentation index at: https://docs.alvys.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tender Events

> Reference for Alvys tender webhook events, including tender.created and tender.change.created, event payloads, the published change-type vocabulary, and subscription behavior.

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.

| Event                     | Fires when                                                            | `data` fields beyond `tenderId`                           |
| ------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------- |
| `tender.created`          | A new inbound tender is received, before anyone reviews or accepts it | `tender`                                                  |
| `tender.change.created`   | A change tender is received for an existing tender                    | `queuedForReview`, `changeCount`, `changeTypes`, `tender` |
| `tender.accepted`         | A tender is accepted                                                  | `loadId`, `loadNumber`, `tender`                          |
| `tender.rejected`         | A tender is rejected                                                  | `tender`                                                  |
| `tender.cancelled`        | A tender is cancelled                                                 | `tender`                                                  |
| `tender.change.accepted`  | Queued tender changes are applied                                     | `tender`                                                  |
| `tender.bid.submitted`    | A bid is submitted for a tender                                       | `bidAmount`, `tender`                                     |
| `tender.stop.arrived`     | A stop arrival is recorded                                            | `stopId`, `arrivedAt`, `reason`, `tender`                 |
| `tender.stop.departed`    | A stop departure is recorded                                          | `stopId`, `departedAt`, `reason`, `tender`                |
| `tender.stop.eta_updated` | A stop's estimated time of arrival is updated                         | `stopId`, `estimatedAt`, `reason`, `tender`               |
| `tender.invoiced`         | A tender is invoiced                                                  | `invoiceId`, `tender`                                     |

The authoritative list of valid event-type strings for your account is returned by [Get event types](/en/api/reference/webhooks/get-event-types) (`GET /api/p/v1/webhooks/event-types`).

<Warning>
  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](/en/api/reference/webhooks/update-webhook).
</Warning>

## Payload

Tender events use the standard Alvys webhook [envelope](/en/api/reference/webhooks/event-delivery-reliability#event-request-format), 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](/en/api/reference/tenders/get-tender) returns. Event-specific fields sit alongside it.

```json theme={null}
{
  "id": "d9a958b3-77cb-43fb-bdc7-ec7e027e689c-2",
  "type": "tender.accepted",
  "timestamp": "2026-02-23T15:19:35.8794642+00:00",
  "version": "v2",
  "etag": "99005ab4-0000-0300-0000-699c72880000",
  "data": {
    "tenderId": "d9a958b3-77cb-43fb-bdc7-ec7e027e689c",
    "loadId": "3cd0060e-ef75-446c-00e4-e9815f6e0000",
    "loadNumber": "1000580",
    "tender": { "...": "full current Tender snapshot" }
  }
}
```

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.

```json theme={null}
{
  "id": "d9a958b3-77cb-43fb-bdc7-ec7e027e689c-0",
  "type": "tender.created",
  "timestamp": "2026-02-23T15:30:07.4523837+00:00",
  "version": "v2",
  "etag": "99005ab4-0000-0300-0000-699c72880000",
  "data": {
    "tenderId": "d9a958b3-77cb-43fb-bdc7-ec7e027e689c",
    "tender": { "...": "full current Tender snapshot" }
  }
}
```

<Info>
  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.
</Info>

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.

| `queuedForReview` | What happened                                                                | `data.tender` holds                        | `changeCount` / `changeTypes` |
| ----------------- | ---------------------------------------------------------------------------- | ------------------------------------------ | ----------------------------- |
| `true`            | The tender has a linked load, so the changes are queued and await acceptance | The tender **before** the proposed changes | Present                       |
| `false`           | The tender has no linked load, so its fields were replaced outright          | The tender **after** the change            | Omitted                       |

```json theme={null}
{
  "id": "d9a958b3-77cb-43fb-bdc7-ec7e027e689c-2",
  "type": "tender.change.created",
  "timestamp": "2026-02-23T15:30:07.4523837+00:00",
  "version": "v2",
  "etag": "99005ab4-0000-0300-0000-699c72880000",
  "data": {
    "tenderId": "d9a958b3-77cb-43fb-bdc7-ec7e027e689c",
    "queuedForReview": true,
    "changeCount": 3,
    "changeTypes": ["LoadRate", "StopSchedule"],
    "tender": { "...": "full Tender snapshot, before the proposed changes" }
  }
}
```

<Warning>
  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](/en/api/reference/tenders/accept-tender-updates) (`POST /api/p/v1/tenders/{tenderId}/accept-updates`) and read the tender again to see the applied values.
</Warning>

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:

<CodeGroup>
  ```text Tender-level theme={null}
  LoadRate
  TotalWeight
  Trailer
  PaymentMethod
  PalletQuantity
  Distance
  TenderNotes
  TenderCharges
  AccessorialsUpdate
  PurchaseOrderNumber
  TenderReferenceAdded
  TenderReferenceRemoved
  ```

  ```text Stop-level theme={null}
  StopSchedule
  StopAddress
  StopAdded
  StopRemoved
  StopNotes
  StopPositionChanged
  StopOrderDetailsChanged
  StopPoNumberChanged
  StopInfoChanged
  StopReferenceAdded
  StopReferenceRemoved
  ```
</CodeGroup>

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](/en/api/reference/webhooks/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.
