Skip to content

Developers

Webhooks

Demurly posts a signed JSON event to your server the moment a record changes, so you don't have to poll. Here's how to receive them safely.

On this page

Set up an endpoint

An admin adds endpoints under Settings > Webhooks > Add endpoint: an https URL that's reachable from the internet, and the events to send (or all of them). Up to 10 endpoints per company. Adding or editing one needs two-step sign-in (2FA). Carriers need Pro ($199 a month) to set this up; upgrade on Settings > Plan & Billing. Fuel buyers get it free.

Demurly shows the endpoint's signing secret (whsec_...) once. Store it next to your API key, as a secret on your server. You can make a new one at any time without missing a delivery: see roll the signing secret. Endpoints can also be added and changed through the API.

What a delivery looks like

Each event is a POST to your URL with a JSON body and these headers. Answer with any 2xx within 10 seconds; do slow work after you answer (put the event on a queue).

HeaderExampleWhat it is
X-Demurly-Signaturet=1768500600,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdThe signature: t is the Unix time it was signed, v1 the hex HMAC-SHA256 of "<t>.<raw body>" keyed with the endpoint's signing secret. Signed afresh on every attempt. After the secret is rolled there's a second v1 (made with the old secret) until the old one expires: accept the delivery if any v1 matches.
X-Demurly-Eventrecord.approvedThe event type (the same as the body's type), to route before parsing.
X-Demurly-Delivery0b8f4f5e-2c7a-4d1e-9b3a-6f2e8d1c4a57This delivery's id: one per event per endpoint, the same on its retries. The delivery log shows it.
Content-Typeapplication/jsonThe body is JSON (UTF-8).
User-AgentDemurly-Webhooks/1.0Lets you tell Demurly's calls apart in your logs.

The body is { id, type, created_at, record_id, sequence, occurred_at, data: { record, event } }: data.record is the record as the API returns it (the record object), and data.event says what happened, who did it (actor_type) and through what (source: web, api, guest_link...). The full shape is the webhook event object, and each event's example is below.

Verify the signature

Check every delivery before you trust it. The signature proves it came from Demurly and wasn't changed:

  1. Read the raw request body, as bytes, before any JSON parsing. Re-serialized JSON won't match.
  2. Split X-Demurly-Signature on commas into t and the v1 values. There can be more than one v1 (while a rolled secret's overlap lasts); keep them all.
  3. Compute the HMAC-SHA256 of <t>.<raw body> with your endpoint's secret (the whole whsec_... string) as the key, as lowercase hex.
  4. Compare it with each v1 in constant time (not ==). The delivery is genuine if any of them matches.
  5. Reject it if t is more than 5 minutes (300 seconds) from your clock, either way. That stops an old delivery being replayed. Keep your server's clock synced (NTP).

Then drop the event if you've already handled its id. These verifiers are tested against real signed deliveries; copy the one for your language.

Verify a delivery
// Verify a Demurly webhook in Node.js (18+), no dependencies.
//
// X-Demurly-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
// keyed with your endpoint's signing secret (whsec_...). Check the raw body
// exactly as it arrived: parsing and re-serializing the JSON changes the bytes.
// After you roll the secret there are two v1 values until the old secret
// expires (t=...,v1=<new>,v1=<old>): accept the delivery if ANY v1 matches.
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyDemurlySignature(rawBody, header, secret, { toleranceSeconds = 300, now = Date.now() } = {}) {
  if (!header || !secret) return false;
  let timestamp = null;
  const signatures = [];
  for (const part of String(header).split(",")) {
    const i = part.indexOf("=");
    if (i < 0) continue;
    const key = part.slice(0, i).trim();
    const value = part.slice(i + 1).trim();
    if (key === "t") timestamp = value;
    else if (key === "v1") signatures.push(value);
  }
  if (!timestamp || !/^\d+$/.test(timestamp) || signatures.length === 0) return false;
  // Reject old (or far-future) timestamps so a captured request can't be replayed
  if (Math.abs(now / 1000 - Number(timestamp)) > toleranceSeconds) return false;

  const body = Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(String(rawBody), "utf8");
  const expected = createHmac("sha256", secret).update(`${timestamp}.`).update(body).digest();
  return signatures.some((sig) => {
    if (!/^[0-9a-f]{64}$/i.test(sig)) return false;
    return timingSafeEqual(Buffer.from(sig, "hex"), expected);
  });
}

// Usage with Node's http server (or express.raw({ type: "application/json" })):
//
//   import http from "node:http";
//   http.createServer((req, res) => {
//     const chunks = [];
//     req.on("data", (c) => chunks.push(c));
//     req.on("end", () => {
//       const raw = Buffer.concat(chunks);
//       if (!verifyDemurlySignature(raw, req.headers["x-demurly-signature"], process.env.DEMURLY_WEBHOOK_SECRET)) {
//         res.writeHead(400).end();
//         return;
//       }
//       const event = JSON.parse(raw.toString("utf8"));
//       // Answer 2xx fast (within 10 seconds); drop an event.id you've already handled
//       res.writeHead(200).end();
//     });
//   }).listen(3000);

Roll the signing secret

Make a new secret with Roll signing secret in the endpoint's menu (Settings > Webhooks) or POST /api/v1/webhook_endpoints/{id}/roll_secret, and choose how long the old one keeps working: immediately, 1 hour, 24 hours, 7 days (the default is 24 hours). Until then every delivery carries two signatures, one per secret, newest first:

X-Demurly-Signature: t=1768500600,v1=<new>,v1=<old>
  1. Roll the secret and copy the new one (it's shown once).
  2. Deploy it to your receiver. A receiver that accepts any matching v1 (the samples above do) verifies with either secret, so nothing fails in between.
  3. When the overlap ends, deliveries carry only the new signature. Roll "immediately" if the old secret leaked.

Rolling again during an overlap stops the older secret at once; only the one just replaced keeps working. The endpoint shows until when the old secret is still sent (previous_secret_expires_at in the API).

Delivery rules

  • At least once. An event can arrive more than once (a retry after your answer was lost, or a resend). Its id is the same every time: keep the ids you've handled and drop repeats.
  • Unordered. Each delivery retries on its own schedule, so an older event can arrive after a newer one for the same record. sequence is the event's place in its record's history and only goes up: keep the highest you've applied per record_id and ignore lower ones, or fetch the record with GET /api/v1/records/{id} for its current state.
  • A snapshot. data.record is the record when the body was first built (the first attempt; retries send the same bytes), not necessarily at the moment of the event.
  • Retries. Anything but a 2xx within 10 seconds (an error status, a timeout, a refused connection) is retried 7 times, after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and 1 day: 8 attempts over about 45 hours. Redirects aren't followed. After the last attempt the delivery is marked failed; you can still resend it from the delivery log.
  • Additive. New event types and new fields can be added. Ignore types and fields you don't know, and don't subscribe to "all events" if you can't.

When an endpoint is turned off

Demurly turns an endpoint off only when both are true: it has accepted nothing for 3 days, and at least 5 events in a row failed every retry. Each event is retried for about 45 hours, so an outage of hours never turns it off, and any delivered event resets the count.

Your company's admins get an email and an in-app notice. Nothing is lost: deliveries waiting for it pause, and events while it's off are kept (shown as Paused in the delivery log; the same happens when an admin turns it off). Fix your receiver, turn the endpoint back on under Settings > Webhooks, and choose Send the missed events (or skip them). They arrive with their original id and sequence, so your duplicate and ordering checks still work.

Testing your receiver

Send test on an endpoint posts a webhook.test event, signed like any other, once (no retries). The delivery log shows every event sent with the body, and each attempt: the request headers (with the signature), how long it took, and your endpoint's status, headers and first 2 KB of its answer. Resend sends one again.

webhook.test body
{
  "id": "7d3e1c2b-9a8f-4e6d-b5c4-a3b2c1d0e9f8",
  "type": "webhook.test",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": null,
  "sequence": null,
  "occurred_at": "2026-01-16T15:20:00.412Z",
  "livemode": true,
  "data": {
    "message": "Test event from Demurly settings"
  }
}

The URL must be public https. To receive events on your own machine while you build, use a tunnel (such as ngrok or Cloudflare Tunnel) and add it as a test endpoint (Settings > Webhooks, mode Test). A test endpoint gets only test-mode events, the ones your test keys and the simulation endpoints cause in your sandbox, each with "livemode": false; a live endpoint never gets them, and real events never reach a test endpoint. Test endpoints are free for every company. See testing your integration.

Polling as a safety net

Webhooks tell you something changed; the API is the record of truth. Many integrations also poll with updated_since every few minutes to catch anything missed. Keep your system in sync shows both together.

Manage endpoints with the API

A TMS or ERP that serves many companies can set up its own endpoint instead of asking an admin to paste a URL. Give an API key the webhooks:write scope (webhooks:read to only look) and use POST /api/v1/webhook_endpoints: it returns the signing secret once, like Settings does. The same rules apply: 10 endpoints at most, a public https URL, events your side gets, and Pro for carriers.

Every change is in your company's audit log as the key's creator, naming the key. A live key manages live endpoints and a test key test endpoints.

Events

Carriers get events about their records; fuel buyers about records sent to them. The record lifecycle shows which move sends which event.

EventSent whenCarriersFuel buyers
record.createdA record was started: a driver started the clock, or a wait was reported through the API, EDI or an import (carriers only)YesNo
record.stoppedThe driver stopped the clock; the record is a draft until submitted (carriers only)YesNo
record.submittedA record stopped and is waiting for the carrier's review (carriers only)YesNo
record.rejected_internalThe carrier's review rejected its driver's record; the buyer never sees it (carriers only)YesNo
record.reopenedThe carrier took a review rejection back; the record is under review again (carriers only)YesNo
record.sentThe carrier sent a record to the buyer for approvalYesYes
record.approvedThe buyer approved a recordYesYes
record.disputedThe buyer disputed a record, with a reasonYesYes
record.escalatedThe carrier escalated a disputeYesYes
record.closedA disputed record was closed without paymentYesYes
record.offer_acceptedThe carrier accepted the buyer's counter-offer from a dispute; the charge is back with the buyer at that amount, to approveYesYes
record.withdrawnA charge you were sent was sent to another buyer or taken back for review by the carrier; data.record is the tombstone the API shows you (buyers only)NoYes
record.cancelledThe carrier cancelled a record (buyers hear only of records sent to them, and get this, not record.withdrawn)YesYes
record.updatedA record's details or billed times changed (a field edit, or times adjusted, which changes the amount); data.event.type says whichYesYes
record.comment_addedSomeone on the carrier or the buyer side commented on a recordYesYes
record.payment_pendingA payment is on its way: a bank payment started in Demurly, or the buyer marked it paid outside Demurly (method and reference in data.event.data)YesYes
record.payment_failedA pending payment didn't arrive: a bank payment failed, or the carrier said an outside payment was never received; the record is approved and unpaid againYesYes
record.paidA record was paid by card or confirmed paid outside DemurlyYesYes
record.payment_reversedA card payment was fully refunded or lost to a chargeback; the record is approved and unpaid againYesYes

record.created

A record was started: a driver started the clock, or a wait was reported through the API, EDI or an import (carriers only). data.event.type: record_created.

record.created body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000047",
  "type": "record.created",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 47,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "recording",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": null,
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": null,
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": null,
      "free_time_minutes": 120,
      "billable_minutes": null,
      "hourly_rate": 100,
      "amount": null,
      "amount_approved": null,
      "stop_reason": null,
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": null,
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": null,
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "record_created",
      "sequence": 47,
      "actor_type": "driver",
      "source": "android_app",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {}
    }
  }
}

record.stopped

The driver stopped the clock; the record is a draft until submitted (carriers only). data.event.type: gps_tracking_stopped.

record.stopped body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000048",
  "type": "record.stopped",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 48,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "draft",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": null,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": null,
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "gps_tracking_stopped",
      "sequence": 48,
      "actor_type": "driver",
      "source": "android_app",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {}
    }
  }
}

record.submitted

A record stopped and is waiting for the carrier's review (carriers only). data.event.type: record_submitted.

record.submitted body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000049",
  "type": "record.submitted",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 49,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "pending_review",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": null,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": null,
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "record_submitted",
      "sequence": 49,
      "actor_type": "driver",
      "source": "android_app",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {}
    }
  }
}

record.rejected_internal

The carrier's review rejected its driver's record; the buyer never sees it (carriers only). data.event.type: employer_rejected.

record.rejected_internal body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000050",
  "type": "record.rejected_internal",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 50,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "rejected_by_employer",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": null,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": null,
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "employer_rejected",
      "sequence": 50,
      "actor_type": "carrier_admin",
      "source": "web",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {
        "reason": "Driver was on a break, not waiting at the rack"
      }
    }
  }
}

record.reopened

The carrier took a review rejection back; the record is under review again (carriers only). data.event.type: record_reopened.

record.reopened body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000051",
  "type": "record.reopened",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 51,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "pending_review",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": null,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": null,
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "record_reopened",
      "sequence": 51,
      "actor_type": "carrier_admin",
      "source": "web",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {}
    }
  }
}

record.sent

The carrier sent a record to the buyer for approval. data.event.type: record_sent_to_customer.

record.sent body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000052",
  "type": "record.sent",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 52,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "sent_to_customer",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": null,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "record_sent_to_customer",
      "sequence": 52,
      "actor_type": "carrier_admin",
      "source": "api",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {}
    }
  }
}

record.approved

The buyer approved a record. data.event.type: record_approved or record_partially_approved.

record.approved body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000053",
  "type": "record.approved",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 53,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "customer_approved",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": 125,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": "2026-01-16T15:20:00.000Z",
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "record_approved",
      "sequence": 53,
      "actor_type": "customer_admin",
      "source": "api",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {
        "approvedAmount": 125
      }
    }
  }
}

record.disputed

The buyer disputed a record, with a reason. data.event.type: record_disputed or record_rejected.

record.disputed body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000054",
  "type": "record.disputed",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 54,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "disputed",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": null,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "record_disputed",
      "sequence": 54,
      "actor_type": "customer_admin",
      "source": "web",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {
        "reason": "Gate log shows a 90-minute wait",
        "disputeType": "time",
        "proposedAmount": 75
      }
    }
  }
}

record.escalated

The carrier escalated a dispute. data.event.type: record_escalated.

record.escalated body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000055",
  "type": "record.escalated",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 55,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "escalated",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": null,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "record_escalated",
      "sequence": 55,
      "actor_type": "carrier_admin",
      "source": "web",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {}
    }
  }
}

record.closed

A disputed record was closed without payment. data.event.type: record_closed.

record.closed body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000056",
  "type": "record.closed",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 56,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "closed",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": null,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "record_closed",
      "sequence": 56,
      "actor_type": "carrier_admin",
      "source": "web",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {}
    }
  }
}

record.offer_accepted

The carrier accepted the buyer's counter-offer from a dispute; the charge is back with the buyer at that amount, to approve. data.event.type: dispute_offer_accepted.

record.offer_accepted body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000057",
  "type": "record.offer_accepted",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 57,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "sent_to_customer",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": null,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "dispute_offer_accepted",
      "sequence": 57,
      "actor_type": "carrier_admin",
      "source": "web",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {}
    }
  }
}

record.withdrawn

A charge you were sent was sent to another buyer or taken back for review by the carrier; data.record is the tombstone the API shows you (buyers only). data.event.type: record_withdrawn.

record.withdrawn body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000058",
  "type": "record.withdrawn",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 58,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "withdrawn",
      "withdrawn_reason": "reassigned",
      "withdrawn_at": "2026-01-16T15:20:00.000Z",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "record_withdrawn",
      "sequence": 58,
      "actor_type": "system",
      "source": "system",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {
        "withdrawn_reason": "reassigned"
      }
    }
  }
}

record.cancelled

The carrier cancelled a record (buyers hear only of records sent to them, and get this, not record.withdrawn). data.event.type: record_cancelled.

record.cancelled body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000059",
  "type": "record.cancelled",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 59,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "cancelled",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": null,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "record_cancelled",
      "sequence": 59,
      "actor_type": "carrier_admin",
      "source": "web",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {}
    }
  }
}

record.updated

A record's details or billed times changed (a field edit, or times adjusted, which changes the amount); data.event.type says which. data.event.type: field_updated, field_corrected or time_adjusted.

record.updated body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000060",
  "type": "record.updated",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 60,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "sent_to_customer",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": null,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "field_updated",
      "sequence": 60,
      "actor_type": "carrier_admin",
      "source": "web",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {
        "field": "bol_number",
        "value": "BOL-99812"
      }
    }
  }
}

record.comment_added

Someone on the carrier or the buyer side commented on a record. data.event.type: comment_added.

record.comment_added body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000061",
  "type": "record.comment_added",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 61,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "sent_to_customer",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": null,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": null,
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "comment_added",
      "sequence": 61,
      "actor_type": "customer_admin",
      "source": "api",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {
        "comment": "Matched to invoice 4471 in our ERP"
      }
    }
  }
}

record.payment_pending

A payment is on its way: a bank payment started in Demurly, or the buyer marked it paid outside Demurly (method and reference in data.event.data). data.event.type: payment_processing or external_payment_marked.

record.payment_pending body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000062",
  "type": "record.payment_pending",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 62,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "payment_pending",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": 125,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": "2026-01-16T15:20:00.000Z",
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "payment_processing",
      "sequence": 62,
      "actor_type": "customer_admin",
      "source": "web",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {
        "payment_method": "us_bank_account"
      }
    }
  }
}

record.payment_failed

A pending payment didn't arrive: a bank payment failed, or the carrier said an outside payment was never received; the record is approved and unpaid again. data.event.type: payment_failed or external_payment_rejected.

record.payment_failed body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000063",
  "type": "record.payment_failed",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 63,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "customer_approved",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": 125,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": "2026-01-16T15:20:00.000Z",
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "payment_failed",
      "sequence": 63,
      "actor_type": "system",
      "source": "webhook",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {
        "method": "us_bank_account",
        "failureCode": "insufficient_funds",
        "failureMessage": "The bank account had insufficient funds."
      }
    }
  }
}

record.paid

A record was paid by card or confirmed paid outside Demurly. data.event.type: payment_completed, external_payment_confirmed or external_payment_recorded.

record.paid body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000064",
  "type": "record.paid",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 64,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "paid_stripe",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": 125,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": "2026-01-16T15:20:00.000Z",
      "paid_at": "2026-01-20T16:00:00.000Z",
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "payment_completed",
      "sequence": 64,
      "actor_type": "system",
      "source": "webhook",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {}
    }
  }
}

record.payment_reversed

A card payment was fully refunded or lost to a chargeback; the record is approved and unpaid again. data.event.type: payment_reversed.

record.payment_reversed body
{
  "id": "e1a2b3c4-0000-4000-8000-000000000065",
  "type": "record.payment_reversed",
  "created_at": "2026-01-16T15:20:00.412Z",
  "record_id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
  "sequence": 65,
  "occurred_at": "2026-01-16T15:20:00.000Z",
  "livemode": true,
  "data": {
    "record": {
      "id": "3f6c1d2e-8a4b-4c1e-9f0a-1b2c3d4e5f60",
      "record_number": "DMR-00001042",
      "status": "customer_approved",
      "carrier": {
        "id": "a1b2c3d4-0000-4000-8000-000000000001",
        "name": "Acme Trucking"
      },
      "customer": {
        "id": "a1b2c3d4-0000-4000-8000-000000000002",
        "name": "Gulf Fuel Supply"
      },
      "terminal": {
        "id": "a1b2c3d4-0000-4000-8000-000000000003",
        "name": "Houston Terminal",
        "time_zone": "America/Chicago"
      },
      "load_number": "L-55821",
      "bol_number": "BOL-99812",
      "container_number": null,
      "start_time": "2026-01-15T14:30:00.000Z",
      "end_time": "2026-01-15T17:45:00.000Z",
      "time_zone": "America/Chicago",
      "time_zone_source": "terminal",
      "start_time_local": "2026-01-15T08:30:00.000-06:00",
      "end_time_local": "2026-01-15T11:45:00.000-06:00",
      "local_start_date": "2026-01-15",
      "adjusted_start_time": null,
      "adjusted_end_time": null,
      "time_adjustment_reason": null,
      "total_minutes": 195,
      "free_time_minutes": 120,
      "billable_minutes": 75,
      "hourly_rate": 100,
      "amount": 125,
      "amount_approved": 125,
      "stop_reason": "manual",
      "started_offline": false,
      "start_received_at": null,
      "start_clock_drift_seconds": null,
      "gps_evidence": {
        "status": "complete",
        "source": "phone",
        "point_count": 42,
        "issues": []
      },
      "source": "phone",
      "source_label": "Recorded by the driver's phone.",
      "vehicle": null,
      "evidence_hash": null,
      "sent_to_customer_at": "2026-01-15T18:10:00.000Z",
      "approved_at": "2026-01-16T15:20:00.000Z",
      "paid_at": null,
      "created_at": "2026-01-15T14:30:02.000Z",
      "updated_at": "2026-01-16T15:20:00.000Z"
    },
    "event": {
      "type": "payment_reversed",
      "sequence": 65,
      "actor_type": "system",
      "source": "webhook",
      "occurred_at": "2026-01-16T15:20:00.000Z",
      "data": {}
    }
  }
}
NextErrorsEvery error code: what causes it, how to fix it and whether to retry.
Webhooks guide