Skip to main content
Use the Tender API to send loads from your system to a carrier that runs on Alvys. You send each load as a JSON tender. The carrier accepts the tender, Alvys creates a load from it, and you follow that load with webhooks. You do not need an EDI network or an X12 identity. If you already exchange EDI 204s, the same endpoints carry the same data as JSON. This guide has two readers:
  • Partner: the TMS, broker, or shipper system that sends tenders.
  • Carrier admin: the person who sets up the connection in the carrier’s Alvys account.
Each section says which reader it is for.
Getting API access
  • Existing Alvys customers can obtain API access by contacting their account representative.
  • Independent Software Vendors (ISVs) should contact the Alvys Partnership team.

What you need

For the partner and the carrier admin. The carrier’s plan must include Public API access. If it does not, every call returns 402.

Scopes

The credential decides the scopes. The scope field in the token request cannot narrow them. Ask the carrier admin to assign these scopes:
Updating and cancelling a tender needs tender:create, not tender:update.
All paths below are relative to the base URL https://integrations.alvys.com. Tender, webhook, location, and subsidiary endpoints use v1.0. Load endpoints use v2.0. See Versioning.

How Alvys finds the carrier and the customer

For the partner and the carrier admin. Every tender carries six routing IDs. They tell Alvys which carrier connection the tender belongs to, and through that connection, which of the carrier’s customers the load is for. The carrier stores the same six values on a connection in Alvys. Alvys compares them as exact strings. Case, spaces, and padding all count. ZZ and zz are different values. A connection has one of two types:
  • Per-customer connection. The connection names one customer. Every tender that matches its six IDs is booked to that customer.
  • Catch-all connection. One connection takes tenders from any sender. Alvys books each tender to the customer whose Company Number equals the tender’s SenderGsId. Exactly one customer must have that Company Number.
Alvys looks for a connection in this order:
  1. A connection whose six IDs all equal the tender’s six IDs.
  2. If there is none, the carrier’s catch-all connection.
  3. If there is none, the request fails with Integration is missing.
Other identifiers:
  • ShipmentId is your identifier for the tender. Alvys uses it to find the tender when you send an update or a cancellation. It becomes the order number of the load.
  • SCAC is stored on the tender. It does not route the tender.
  • Stop IdCode values do not select a location. Alvys matches stop locations by address and name.

Without EDI: choose the routing IDs

For the partner and the carrier admin. You do not need an X12 identity. Agree on six values with the carrier, and the carrier enters them on the connection. Use the qualifier ZZ (mutually defined) and short, uppercase IDs without spaces.
If the carrier uses a catch-all connection, SenderGsId must equal the Company Number of the customer in Alvys. If no customer has that Company Number, or more than one does, the tender is rejected.

With EDI: reuse your X12 IDs

For partners that already exchange EDI 204s. Send your real ISA and GS IDs and qualifiers. The JSON endpoints map to the 204 purpose codes: Differences from classic X12 over a VAN or AS2:
Alvys finds the connection by the six routing IDs only. It does not check the connection’s transport. If your IDs match a connection that uses an EDI transport, such as AS2, SFTP, or a VAN, Alvys sends EDI responses such as 990s over that connection, the same as for an EDI tender. Ask the carrier to set up a separate connection with the Public API transport for your API traffic, with routing IDs that no EDI connection uses.

Carrier setup checklist

For the carrier admin.
  1. Create API client credentials for the partner under Admin → API Access and assign the scopes above.
  2. Create the customer the loads are for. If you use a catch-all connection, set the customer’s Company Number to the agreed SenderGsId.
  3. Go to Manage → EDI & Visibility and add a connection for the partner:
    • Set the transport to Public API.
    • Enter the six routing IDs.
    • Choose the customer, or make it a catch-all connection.
    • Choose the subsidiary the loads belong to.
  4. Decide how tenders are accepted. To accept them automatically, turn on Auto-Accept EDI Tenders on the customer. If you use fleets, also set the customer’s default fleet.
  5. Send the partner the six routing IDs and your SCAC.
A connection with the Public API transport sends nothing back over EDI. The partner gets every outcome through webhooks or by polling the API.

Workflow

For the partner. A tender moves through these statuses:

1. Get a token

Request a token with the client credentials grant and send it as Authorization: Bearer <token> on every call. Cache the token until it expires. See Authenticate with the Alvys Public API.

2. Subscribe to tender and load events

Create the subscription before you send the first tender, so you do not miss tender.created.
  • List every event by its exact name. There are no wildcards.
  • Send exactly one of SubsidiaryIds or AllSubsidiaries. SubsidiaryId is deprecated. Get subsidiary ids from List subsidiaries.
  • A subscription can hold up to 50 events.
The 201 response includes Secret. Store it. Get and list calls return it masked. Use it to verify signatures.
The response above is shortened. See Create webhook subscription for every field.

3. Create a tender

POST /api/p/v1.0/tenders creates a tender in the carrier’s account. The smallest valid tender has the routing IDs, SCAC, ShipmentId, and two stops:
A successful call returns 201 Created with an empty body. The Location header points to the new tender:
A fuller tender adds equipment, weight, rate, references, and notes:
The qualifier and unit codes in this example (L, TV, BM, PU, DO) are common X12 values. Alvys does not validate them. Rules for the tender body:
  • Send at least two stops. Each stop Type is Pickup or Delivery.
  • Include at least one pickup and one delivery whose Entity has a city and a state. If not, Alvys still returns 201, but the tender cannot be accepted until the carrier fixes it in Alvys.
  • Set Type on each reference to the reference name the carrier uses, for example BOL Number. The name becomes the reference name on the load. A reference without Type can be dropped.
  • Rate becomes the linehaul and billing rate on the load. You cannot send fuel surcharge or accessorial charges on a tender.
  • The response does not include the tender. Read it in the next step.
See Create tender for every field.

Dates and time zones

Send each date as the local wall-clock time at the stop or at the customer. Alvys keeps the clock time you send and applies the time zone it resolves for the tender. A UTC timestamp, such as one ending in Z, can therefore move by several hours. Alvys ignores the offset in your timestamps. The -06:00 in the examples on this page has no effect. If Alvys cannot resolve a time zone for the tender, it drops ExpirationDate, and the tender shows no response deadline.

4. Find your tender id

You need the tender id to read the tender and to accept or reject it. Get it in one of three ways:
  • The Location header of the create response.
  • data.tenderId in the tender.created webhook.
  • A search by ShipmentId:
The response is a page with Items, Total, Page, and PageSize. Each item is a tender with its Id, Status, ShipmentId, and stops.
The tender id usually stays the same. It can change if Alvys links the tender to an existing load. Take the id from the latest webhook, or search by ShipmentId, before you act on a tender.

5. Accept the tender

A tender is accepted in one of three ways. The carrier chooses which. When Alvys creates a load from the tender, it sends tender.accepted with the new load’s loadId and loadNumber. The new load gets the tender’s id, so loadId equals tenderId:
The data.tender snapshot above is shortened. It has the same fields as Get tender, in camelCase. See Tender events.
If the carrier already has a load whose order number equals your ShipmentId, Alvys links the tender to that load and does not create a new load. The tender takes the id of that load. Alvys then sends tender.created for the new tender id, with data.tender.status set to Accepted, and does not send tender.accepted. Treat a tender.created with status Accepted as an acceptance, and match it to your tender by shipmentId.
If auto-accept fails, for example because a stop location cannot be matched or the customer has no default fleet, the tender stays New. The reason is shown to the carrier in Alvys. It is not in the API response.

Accept through the API

  1. Get the tender and read the StopId of each stop:
  2. Find the Alvys location id for each stop with Get location or Search locations. The location must already exist. Accepting through the API does not create locations.
  3. Accept with one link for every stop on the tender. FleetId is optional.
The response is 200 with the tender. Status is Accepted and LoadNumber is set. If you accept a tender that is already accepted, Alvys returns it unchanged.

6. Read the load

After acceptance, the tender has a load. Get the load by its order number, which is your ShipmentId, or by the loadNumber from tender.accepted. Send exactly one query parameter.
The response above is shortened. See Get load for every field.

7. Send changes and cancellations

Send a change to POST /api/p/v1.0/tenders/update and a cancellation to POST /api/p/v1.0/tenders/cancel.
  • Send the full tender body each time, with the same routing IDs and ShipmentId, and the full list of stops. For a cancellation, send the body of the last tender you sent.
  • Both return 201 with an empty body, like create.
  • If no tender exists for the ShipmentId, or the latest tender was cancelled, an update creates a new tender.
  • If no tender exists for the ShipmentId, a cancellation creates a tender and cancels it, so the carrier sees the cancellation in Alvys.
  • If you send a create again for a ShipmentId whose latest tender is not cancelled or rejected, Alvys treats it as an update. This includes a tender that is already accepted. If the latest tender was cancelled or rejected, or its load was cancelled, Alvys creates a new tender.
What happens next:
  • Update. Alvys sends tender.change.created. If queuedForReview is true, the carrier reviews the change in Alvys, and you get tender.change.accepted when it is applied. If it is false, the change was applied directly.
  • Cancel, tender not yet accepted. The tender is cancelled. You get tender.cancelled.
  • Cancel, tender accepted. By default, the carrier confirms the cancellation, and then the load is cancelled. Some connections cancel the load at once, without the carrier’s confirmation. You get tender.cancelled.
  • Cancel, no tender for the ShipmentId. Alvys creates a tender and cancels it. You get tender.created, and then tender.cancelled.
Use a ShipmentId that is unique within the carrier’s account, for example with a prefix for your system. Alvys finds the tender to update or cancel by ShipmentId.

8. Reject a tender

For the carrier, or a partner that acts for the carrier. POST /api/p/v1.0/tenders/{tenderId}/reject rejects a tender. Set CancelLinkedLoad to true to also cancel a load that was already created from it.
Reason codes are free text. Common values are A13 (Other reasons), EQU (No equipment available), CPU (Agreed-upon capacity exceeded), LL1 (Load does not belong to us), and LL2 (Unable to complete the load). The partner gets tender.rejected.

9. Track status and documents

  • Status. load.status.changed carries previousStatus, status, and the load. See Load events.
  • Stop updates. tender.stop.arrived, tender.stop.departed, and tender.stop.eta_updated report stop progress against the tender. See Tender events.
  • Documents. load.document.uploaded carries the load number and a document with a downloadUrl. The link is valid for 10 minutes. For a fresh link, call GET /api/p/v2.0/loads/{loadNumber}/documents. See Document events.
  • Upload documents. POST /api/p/v2.0/loads/{loadNumber}/document uploads a file to the load. See Upload load document.
Webhooks can arrive more than once and out of order. Use X-Alvys-Event-Id to ignore duplicates. To recover events you missed, poll Get load and the documents list on a schedule. See Event delivery reliability.
The REST API uses PascalCase property names, for example LoadNumber. Webhook payloads use camelCase, for example loadNumber, and leave out properties that have no value.

Common errors

Errors are listed with the most frequent first. “Conflict” errors return 400, not 409. Some errors return a plain JSON string instead of a problem object. If none of these match, contact support@alvys.com with the ShipmentId, the time of the call, and the response body.