Webhook Integration Guide
Overview
The TAI TMS webhook system delivers HTTP POST notifications to your endpoint when events occur inside the platform — shipment lifecycle changes, customer and carrier record updates, and accounting document creation.
How It Works
Webhooks are configured through Source Settings in the TMS admin interface. Each webhook integration is an IntegrationSource of type PublicAPIWebhooks, scoped to one or more Linked Organizations. When a triggering event occurs for a record that belongs to a linked organization, the TMS serializes the relevant data and POSTs it as JSON to the URL(s) you have configured.
TMS Event Occurs
│
▼
Linked Organization matches?
│ Yes
▼
(Shipment events only) Batch in ~1-min window
│
▼
Serialize payload (camelCase JSON)
│
▼
POST to configured URL
│
▼
Log result to WebhookActivityLog
Available Webhooks
| Category | Webhook | Trigger |
|---|---|---|
| Shipment | ShipmentCreateUrl | New shipment persisted |
ShipmentDetailUpdateUrl | Any shipment field or child record edited | |
ShipmentStatusUpdateUrl | Status change, stop-date edit, EDI 214, tracking updates | |
ShipmentLocationUpdateUrl | GPS/location ping (15-min throttle) | |
| Accounting | BillCreateUrl | Carrier/vendor bill approved (~5 second delay) |
CommissionBillCreateUrl | Commission approved to Created | |
InvoiceCreateUrl | Customer invoice created | |
InvoiceCreateWithShipmentUrl | Customer invoice created (includes shipment) | |
| Customer | CustomerCreateUrl | New customer organization |
CustomerUpdateUrl | Customer record edited (direct or indirect) | |
| Carrier | LSPCarrierCreateUrl | New broker carrier (Highway onboarding only) |
LSPCarrierUpdateUrl | Broker carrier record edited |
Key Behaviors
| Behavior | Detail |
|---|---|
| HTTP method | POST |
| Content type | application/json |
| Encoding | UTF-8 |
| Property naming | camelCase |
| Date format | ISO 8601 — yyyy-MM-ddTHH:mm:ssK (UTC) |
| Enum serialization | String values (not integers) |
| Retry policy | No automatic retries — each event is attempted once |
| Delivery guarantee | At-most-once. If your endpoint is unavailable or errors, the event is logged and no further delivery is attempted. |
| Transport | HTTPS strongly recommended but not enforced; plain HTTP endpoints are accepted (see Authentication) |
| Custom headers | None. Only Authorization (if configured) plus HttpClient's auto-added Content-Type and Content-Length. No User-Agent, no X-Webhook-*, no HMAC signature. |
| HTTP redirects | 3xx responses are followed automatically |
Shipment Webhook Batching
Shipment-related webhooks (ShipmentCreateUrl, ShipmentDetailUpdateUrl, ShipmentStatusUpdateUrl, ShipmentLocationUpdateUrl) are dispatched via a background queue with a ~1-minute delay after the triggering event. Multiple edits to the same shipment within that window coalesce into a single delivery — the timer resets on each new change and fires ~1 minute after the last change is saved. This prevents duplicate webhooks when users make multiple edits in quick succession.
Accounting, customer, and carrier webhooks are dispatched immediately, with one exception: BillCreateUrl uses a background job with a ~5-second delay.
Versioning
The Version source setting controls how shipment statuses are labeled in the payload. It applies only to shipment webhooks (Create, Detail, Status, Location). Accounting, customer, and carrier payloads are unversioned and always use the current schema.
| Version | Behavior |
|---|---|
| 2 | Statuses transformed: Committed → Booked and Quote → Quoted. All other statuses (Dispatched, InTransit, OutForDelivery, Delivered, etc.) unchanged. |
| 3 (default) | Current format. Statuses sent as-is: Committed and Quote. |
If Version is missing, empty, or set to any value other than "2", the payload is sent in V3 form.
Source Setting Parameters
When configuring a Source Setting of type PublicAPIWebhooks, the following parameters are available:
| Parameter | Type | Description |
|---|---|---|
ShipmentCreateUrl | URL | Endpoint for shipment creation events. |
ShipmentDetailUpdateUrl | URL | Endpoint for shipment detail update events. Also fires alongside ShipmentStatusUpdateUrl. |
ShipmentStatusUpdateUrl | URL | Endpoint for shipment status and transit-related change events. |
ShipmentLocationUpdateUrl | URL | Endpoint for location updates. 15-min throttle. |
BillCreateUrl | URL | Endpoint for bill approval events. Fires with a ~5-second delay. |
CommissionBillCreateUrl | URL | Endpoint for commission approval events. |
InvoiceCreateUrl | URL | Endpoint for invoice creation events (invoice only). |
InvoiceCreateWithShipmentUrl | URL | Endpoint for invoice creation events (invoice + shipment). Fires independently of InvoiceCreateUrl. |
CustomerCreateUrl | URL | Endpoint for customer creation events. |
CustomerUpdateUrl | URL | Endpoint for customer update events (includes indirect triggers). |
LSPCarrierCreateUrl | URL | Endpoint for carrier creation events. Currently limited to Highway onboarding. |
LSPCarrierUpdateUrl | URL | Endpoint for carrier update events. |
Authorization | String | Static authorization token sent verbatim in the Authorization request header. |
UserName | String | Username for HTTP Basic Authentication (paired with Password). |
Password | String | Password for HTTP Basic Authentication (paired with UserName). |
Version | Integer (2 or 3) | Controls shipment status label format. Defaults to 3. |
DoNotMarkInvoiceAsPrinted | Boolean (true/false) | When true, invoices are not marked as printed after the webhook fires. Defaults to false. |
Authentication
All webhook communication is outbound — the TMS sends requests to your endpoint. You are responsible for securing your endpoint; the TMS provides the following mechanisms to prove the request is coming from the platform.
Transport Security
The TMS supports TLS 1.0, 1.1, and 1.2. TLS 1.3 is not currently supported. If your endpoint enforces TLS 1.3-only, webhook delivery will fail at the transport layer.
HTTPS is strongly recommended but not enforced. The TMS will POST to a plain http:// URL if configured — this exposes the Authorization header and payload in transit.
Recommendation: Configure https:// endpoints only, and require TLS 1.2 on your endpoint.
Authentication Methods
Option 1 — Static Authorization Header
Set the Authorization source setting parameter to any string value. The TMS will include it verbatim as the Authorization header on every request. No prefix or transformation is added.
# Bearer token
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# Custom API key
Authorization: ApiKey abc123xyz
# HMAC signature (pre-computed and stored as a static value)
Authorization: HMAC sha256=abc123...
Option 2 — HTTP Basic Authentication
Set both the UserName and Password source setting parameters. The TMS will Base64-encode username:password (ASCII encoding) and attach it as a standard Authorization: Basic header.
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
When Both Are Configured
If both are set, Basic Authentication takes precedence. The effective header sent will be Authorization: Basic <base64(user:pass)>.
| Configuration | Header sent |
|---|---|
Authorization only | Authorization: <your value> |
UserName + Password only | Authorization: Basic <base64(user:pass)> |
| Both set | Authorization: Basic <base64(user:pass)> (Basic wins) |
Verifying Requests Are From the TMS
Because the TMS does not sign payloads with a cryptographic signature (no HMAC, no X-Signature header), the recommended approach is:
- Use a strong secret in the
Authorizationheader. Treat the value as a shared secret — long, randomly generated, validated on every request. - Accept requests only over HTTPS.
- Return
401 Unauthorizedfor missing or incorrect credentials. The TMS will log the response code but will not retry.
Error Handling & Logging
Delivery Behavior
The TMS makes a single attempt to deliver each webhook. There is no automatic retry.
| Scenario | TMS behavior |
|---|---|
| Endpoint returns 2xx | Success — response body and status code logged. |
| Endpoint returns 3xx | Follows the redirect automatically to the new URL. |
| Endpoint returns 4xx or 5xx | Logged as-is — the non-2xx status is recorded but does not trigger a retry or exception. Response body is still captured. |
| Network error / DNS failure | Exception caught — error message logged, no response data captured. |
| Endpoint times out | Exception caught — error message logged. Default HttpClient timeout (~100 seconds). |
| TLS / certificate error | Exception caught — error message logged. |
Important: The TMS does not distinguish between 2xx and non-2xx responses for retry purposes. A 500 from your endpoint is logged just like a 200 — no re-delivery is attempted either way. To signal a real problem, you must proactively monitor WebhookActivityLogs yourself.
Your endpoint should respond quickly. Long-running processing should be dequeued asynchronously — accept the webhook, return 200 OK immediately, and process in the background.
Webhook Activity Logs
Every delivery attempt — successful or not — is written to the WebhookActivityLogs table in the TMS logging database, joined with ApiDetails via ApiDetailId.
| Field | Type | Populated | Description |
|---|---|---|---|
WebhookActivityLogId | long | Always | Auto-incrementing primary key. |
OrganizationId | int | Always | The organization that owns the triggering record. |
StaffId | int | Always for user-initiated events; may reflect a system user ID for background/EDI/driver-tracking events | Staff member whose action triggered the webhook. |
IntegrationSourceId | int? | Usually populated; nullable in schema | The Source Setting that owns this webhook configuration. |
WebhookType | WebhookType? (nullable enum, 1–13) | Always | Which webhook fired. Serialized as an integer in SQL. |
Id | long? | Always | ID of the triggering record — polymorphic per WebhookType. |
StartDateTime | datetime (UTC) | Always | When TMS began processing the webhook. |
EndDateTime | datetime (UTC) | Always | When the delivery attempt completed. |
Duration | int (seconds) | Always | DATEDIFF(SECOND, StartDateTime, EndDateTime). |
ErrorMessage | string | Network-level error only | Exception message. Truncated to 1,000 chars. Absent when any HTTP response was received. |
ApiDetail.Url | string | When an HTTP request was sent | Absolute URL. Truncated to 200 chars. |
ApiDetail.HttpStatusCode | int | When an HTTP response was received | Response code (200, 401, 500, etc.). |
ApiDetail.Request | string | Always | The full JSON payload that was sent. |
ApiDetail.Response | string | When an HTTP response was received | Raw response body (any status code). |
Diagnosing Common Failures
Webhook fired, endpoint returned 401
ErrorMessageis null (an HTTP response was received).ApiDetail.HttpStatusCodeis401.- Check that the
Authorization(or Basic) value matches what your endpoint expects.
Webhook fired but nothing received
- Empty
ApiDetail.ResponsewithHttpStatusCode200 → your endpoint returned an empty body. Fine. ErrorMessagepopulated → network-level failure before any HTTP response (DNS, timeout, TLS).
Webhook never appeared in logs
- Confirm the triggering record's org matches the Source Setting's Linked Organizations.
- For shipment webhooks, wait past the ~1-minute batching window.
- For
ShipmentLocationUpdateUrl, check the 15-minute throttle window hasn't suppressed it. - Log entries are batched (~10s delay before queryable) — see below.
Log Persistence and Batching
Webhook activity logs are flushed to the logging database in batches. A flush occurs when either:
| Condition | Threshold |
|---|---|
| Time since last flush | > 10 seconds |
| Items accumulated | ≥ 200 entries |
Expect up to a ~10-second delay before a delivery attempt is queryable in the logs.
Updated 5 days ago
