Turvo Trigger
The Turvo Trigger starts a workflow when Turvo — a cloud transportation management system (TMS) — reports an event. When a shipment is created or updated, a shipment's status changes, or a customer invoice is created or updated in Turvo, Turvo posts the event to Splice, and this trigger turns it into a workflow execution. Use it to react to Turvo activity in real time: sync shipments into another system, generate and transmit EDI, automate billing, validate data, or raise exceptions as they happen.
Overview
Use the Turvo Trigger when you need to:
- React to shipment lifecycle events - Start a workflow the moment a Turvo shipment is created, changes, or moves to a new status
- Automate billing - Act on customer invoice creation and updates (e.g. push invoices into an accounting system or send an 810)
- Keep systems in sync - Push shipment and invoice changes from Turvo into a WMS, ERP, billing system, or data warehouse
- Drive EDI from Turvo activity - Generate and send EDI (e.g. 204/214/990/810) in response to Turvo events
- Act on full shipment data - Shipment events carry the complete shipment object, so the workflow can branch and transform without an extra lookup
Endpoint & Setup
The Turvo Trigger is delivered over the same HTTP ingress as the Webhook trigger. Turvo posts events to the workflow's public endpoint:
POST https://<your-splice-api-domain>/hook/{workflowId}
Configure a Turvo webhook subscription to send events to this URL (copy it from the workflow), then choose which events should start the workflow with the Action setting below. The events this node consumes are Turvo's standard webhook events — see Turvo's Events documentation for the authoritative event catalog and payload reference.
The trigger node itself takes no credential — it only receives the inbound event. If a workflow needs to call back into Turvo (to fetch related records or push updates), use an HTTP Request or a Turvo service node with a separate Turvo API credential. That credential holds the Environment (Sandbox/Production), Client ID, Client Secret, Username, Password, and API Key used for outbound Turvo API calls.
Configuration
Action (required)
The set of Turvo events that may start the workflow. Select one or more; only the selected event types trigger a run.
| Event | Value | Fires when |
|---|---|---|
| Add Shipment | shipment-create | A new shipment is created in Turvo |
| Update Shipment | shipment-update | An existing shipment is updated in Turvo |
| Shipment Status Update | shipment-status-update | A shipment's status changes |
| Customer Invoice Create | customer-invoice-create | A customer invoice is created |
| Customer Invoice Update | customer-invoice-update | A customer invoice is updated |
Output
The trigger emits a single output keyed by the node's name. The payload shape depends on the event type, and Turvo uses two different envelopes:
- Shipment events (
shipment-create,shipment-update) —{ event, data }, with the full shipment object underdata. - Status events (
shipment-status-update) — primarily{ event, data }with a compact status summary (an alternate{ eventName, eventPayload }form also occurs). - Invoice events (
customer-invoice-create,customer-invoice-update) —{ tenantId, eventName, eventTime, eventPayload }, with the invoice undereventPayload.document.
Because the envelope and fields vary, inspect the captured sample for the events you actually receive before mapping fields downstream.
Shipment events
shipment-create and shipment-update deliver the complete shipment under data:
{
"Turvo Trigger": {
"event": "shipment-update",
"data": {
"id": 42250,
"customId": "31423-79336",
"status": {
"code": { "id": 100162, "key": "2102", "value": "Covered" }
},
"type": { "key": "5000", "value": "FTL" },
"lane": { "start": "Palo Alto, CA", "end": "California City, CA" },
"customerOrder": [ { "id": 42870, "customer": { "id": 149090, "name": "testWebhookEventCust" } } ],
"carrierOrder": [ { "id": 25738, "carrier": { "id": 149091, "name": "testWebhookEventCarrier", "scac": "AAXEW" } } ],
"globalRoute": [ /* stops with address, appointment, stopType, ... */ ],
"items": [ /* commodities */ ]
}
}
}
data- The complete Turvo shipment object. It is large and nested; the most commonly used fields are:data.id- The Turvo shipment ID.data.customId- The human-readable shipment / custom ID.data.status.code.value- The shipment status (e.g.Covered,Tendered,Delivered).data.customerOrder[]/data.carrierOrder[]- Customer and carrier order details, including parties, costs, equipment, invoices, and payments.data.route[]/data.globalRoute[]- The stops, with addresses, appointment windows, stop types, and PO numbers.data.items[]- The commodities on the shipment (weights, dimensions, hazmat, quantities).data.externalIds[]- External reference values such as SCAC and PO numbers.
A shipment event contains hundreds of fields across nested orders, routes, and items. Use a Set or Code node early in the workflow to pull out just the fields you need, and reference them by path (e.g. {{$json.data.status.code.value}}) downstream.
Status events
shipment-status-update delivers a compact status summary rather than the full shipment object:
{
"Turvo Trigger": {
"event": "shipment-status-update",
"data": {
"id": 6677898,
"status": {
"code": { "id": 100164, "key": "2104", "value": "At pickup" },
"description": "At pickup",
"notes": "",
"miles": 1364,
"eta_seconds": 11708339,
"statusDate": { "date": "2024-08-15T14:19:00-04:00", "timezone": "America/New_York" },
"location": { "name": "Fort Myers, FL", "lat": 26.6054192, "lng": -81.8704111 },
"updatedBy": { "id": 1786, "name": "Regression Broker" }
},
"customerOrder": [ { "id": 2367, "customer": { "id": 762 } } ],
"carrierOrder": [ { "id": 1429, "carrier": { "id": 669 } } ]
}
}
}
data.id- The Turvo shipment whose status changed.data.status.code.value- The new status (e.g.At pickup,Delivered);data.status.descriptionis the human-readable label.data.status.location- Where the shipment is, withname,lat, andlng.data.status.miles/data.status.eta_seconds- Remaining distance and ETA to the next stop.data.status.updatedBy/data.status.statusDate- Who changed the status and when.
Turvo's status feed is not perfectly uniform — some integrations emit a different envelope ({ eventName: "SHIPMENT_STATUS_UPDATE", eventTime, eventPayload: { ... } }) instead of { event, data }. Inspect the captured sample for the events you actually receive and read defensively.
Invoice events
customer-invoice-create and customer-invoice-update use Turvo's { tenantId, eventName, eventTime, eventPayload } envelope. The invoice lives under eventPayload.document, alongside the parent shipment's context:
{
"Turvo Trigger": {
"tenantId": "181",
"eventName": "CARRIER_INVOICE_ADD",
"eventTime": "2021-07-14T14:46:48.009Z",
"eventPayload": {
"id": 6889103,
"custom_id": "R3160-12764",
"document": {
"type": "Carrier invoice",
"status": { "id": 110591, "key": "2800", "value": "Draft" },
"invoice": {
"id": "68381591861ddd5d7fdcad6c",
"number": "Carrier-Invoice-01",
"amount": 100,
"date": "2025-05-29T05:00:00.000Z",
"dueDate": "2025-05-30T05:00:00.000Z"
},
"currency": "CAD",
"account": { "id": 2035483, "accountId": 4897112, "name": "CreditMemoCarrier", "type": "CARRIERORDER" },
"memo": { "issuances": [ /* ... */ ], "allocations": [ /* ... */ ] }
},
"carrierOrder": [ /* order, costs, equipment, externalIds, ... */ ],
"status": { "code": { "id": 100162, "key": "2102", "value": "Covered" } },
"lane": { "start": "Los Angeles, CA, US", "end": "Los Angeles, CA, US" },
"totalPayableAmount": 100,
"totalReceivableAmount": 500
}
}
}
eventName-CARRIER_INVOICE_ADD(create) orCARRIER_INVOICE_UPDATE(update).eventPayload.id/eventPayload.custom_id- The shipment the invoice belongs to.eventPayload.document.invoice- The invoice'sid,number,amount,date, anddueDate.eventPayload.document.status.value- The invoice status (e.g.Draft,Approved).eventPayload.document.currency- The invoice currency (a string here, e.g.CAD).eventPayload.document.account- The billed account (name,type).eventPayload.status.code.value- The parent shipment's status.
Invoice events are the least consistent of the Turvo events. They use the { eventName, eventPayload } envelope, and the metadata's bundled sample is actually a carrier invoice (document.type: "Carrier invoice", eventName: CARRIER_INVOICE_ADD) even though the action is labeled "Customer Invoice." Map fields from the sample your Turvo tenant actually sends, not from a fixed shape.
Example Usage & Common Use Cases
Sync a Shipment into Another System
[Turvo Trigger] → [Set: map data.id / customId / status / stops]
→ [HTTP Request: upsert into WMS]
Generate EDI on Shipment Creation
Action: Add Shipment
[Turvo Trigger] → [Set: extract shipment fields] → [EDI: build 204] → [AS2 / FTP: send to carrier]
Branch on Event Type
[Turvo Trigger] → [If: event == "shipment-create"]
↓ true ↓ false (update)
[Create record] [Update record]
Act on Status Changes
Action: Shipment Status Update
[Turvo Trigger] → [If: data.status.code.value == "Delivered"] → [Send 214 / notify customer]
Automate Customer Invoicing
Action: Customer Invoice Create
[Turvo Trigger] → [Set: map invoiceNumber / customer / amount]
→ [EDI: build 810] → [AS2: send to customer]
How It Works
- Turvo posts an event - Turvo sends a
POSTto the workflow's/hook/{workflowId}URL when a subscribed event occurs, including the full shipment payload. - Match the trigger - The webhook ingress resolves the workflow and selects its trigger node.
- Filter by action - Only events whose type is enabled in Action start the workflow.
- Enqueue - The event envelope (
{ event, data }) becomes the trigger output and a workflow execution is started.
Best Practices
- Select only the events you handle - Enabling just the event types the workflow is built for avoids unnecessary runs.
- Extract early - The payload is deep and verbose; map the handful of fields you need up front so downstream nodes stay readable.
- Make downstream actions idempotent -
shipment-updatecan fire repeatedly for the same shipment; key downstream updates ondata.idso repeats are safe. - Handle create/update distinctly - Branch on
eventwhen create and update should do different things (insert vs. upsert). - Guard against missing fields - Optional sections (invoices, payments, holds, items) may be absent or empty depending on the shipment; don't assume a fixed shape.
Troubleshooting
Common Issues
- No workflow runs on a Turvo event - Confirm Turvo's webhook subscription points at the correct
/hook/{workflowId}URL, the workflow is active, and the event type is enabled under Action. - A field is missing from
data- Turvo omits sections that don't apply to a given shipment. Inspect the captured sample for the actual shape and code defensively. - Outbound Turvo API call fails - The trigger and the Turvo API are separate concerns. Verify the Turvo API credential (environment, client ID/secret, username/password, API key) used by the downstream HTTP/service node, not the trigger.
Debugging Tips
- Inspect the captured sample - The trigger output is stored as node sample data on each run; use it to navigate the nested payload and build downstream field paths.
- Test with a manual POST - Send a sample
shipment-create/shipment-updatebody to the URL withcurlto confirm the endpoint and downstream logic before relying on live Turvo events.
Related
- Webhook - The general-purpose HTTP trigger this node is built on
- HTTP Request - Call back into the Turvo API when the workflow needs more than the event payload
- Turvo Events documentation - Turvo's authoritative reference for the webhook events this node receives
- Trigger Nodes - Overview of all available workflow trigger types