Webhooks & Polling

Truemed notifies your systems about dispute activity in real time through three webhook events, delivered over the same signed, retried webhook infrastructure as the rest of the Truemed API. The model is notify, then fetch: the webhook tells you a dispute changed and carries a full snapshot, and you can re-fetch the dispute for authoritative state.

Events

EventFires when
dispute.createdA dispute first becomes visible to you — a new chargeback, or a live inquiry you can respond to (see the inquiry state).
dispute.updatedAn open dispute’s state or phase changes (for example, after you submit evidence and it moves to under_review).
dispute.closedA dispute reaches a terminal state — a chargeback outcome (won, lost, accepted, expired) or a surfaced inquiry the bank drops (closed).

Inquiries notify too — with one exception. A live inquiry fires dispute.created (and dispute.updated) just like a chargeback, so you can respond to pre-empt escalation. If the bank later drops a surfaced inquiry, it reaches the terminal closed state and fires dispute.closed — so dispute.closed marks any terminal outcome, whether a chargeback result (won/lost/accepted/expired) or a dropped inquiry (closed). The one exception: an inquiry that opens and closes between webhook deliveries — already terminal the first time Truemed sees it — fires no webhook at all, since nothing was ever actionable.

Events are deduplicated on transitions: a re-sync of an already-terminal dispute does not re-fire dispute.closed, and an update that doesn’t change state or phase is silent.

Payload

Dispute events use the standard signed webhook envelope: the data field is the full canonical dispute — the same shape returned by GET /api/v1/disputes/{dispute_id}, including dispute_id, state, phase, reason, amount, due_by, counters, and metadata. Because the snapshot is complete, you can often act on the webhook directly; re-fetching is the safe fallback when a delivery is delayed or retried out of order.

1{
2 "webhook_delivery_id": "dlv_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
3 "event_type": "dispute.created",
4 "data": {
5 "dispute_id": "d1f8c2a0-3b4c-4d5e-8f90-1a2b3c4d5e6f",
6 "processor_dispute_id": "du_1AbC2DeFgHiJkLmN",
7 "processor": "STRIPE",
8 "state": "needs_response",
9 "phase": "dispute",
10 "reason": "fraudulent",
11 "network_reason_code": "10.4",
12 "amount": 2000,
13 "currency": "USD",
14 "due_by": "2026-02-01T23:59:59Z",
15 "created_at": "2026-01-15T12:00:00Z",
16 "counters": [],
17 "metadata": {},
18 "evidence_requirements": {
19 "merchant_explanation": "Show the cardholder authorized the purchase and received the goods: signed proof, a matching IP or device, and delivery confirmation.",
20 "compelling": [
21 { "category": "shipping_tracking_number", "label": "Shipping tracking number", "description": "Carrier tracking number showing delivery to the cardholder's address.", "text": true, "file": false },
22 { "category": "customer_signature", "label": "Customer signature", "description": "A signed order form or delivery receipt.", "text": false, "file": true }
23 ],
24 "supporting": [
25 { "category": "receipt", "label": "Receipt", "description": "The order receipt or invoice.", "text": false, "file": true }
26 ],
27 "other": [
28 { "category": "refund_policy", "label": "Refund policy", "description": "Your published refund policy.", "text": false, "file": true }
29 ]
30 },
31 "payment": {
32 "order_id": "c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f"
33 }
34 }
35}

Null fields are omitted, so outcome and resolved_at are absent until the dispute resolves (and prior_dispute_id is absent unless this is a re-filing). processor and currency are upper-case ("STRIPE", "USD").

The delivery is signed with HMAC-SHA256; verify the x-truemed-signature header before processing. The header carries a timestamp and the signature — for example x-truemed-signature: t=1706108400,v0=5257a869… — and the signed message is {t}.{raw_body}. See Signature verification for ready-to-use snippets. Payloads carry canonical Truemed fields and the public dispute_id UUID — never raw processor payloads. Always key your records on dispute_id.

Subscribing

Dispute events use your existing sales-channel webhook configuration and respect its scope (testing vs. non-testing). Add the dispute.* events to your webhook endpoint the same way you manage other Truemed webhook subscriptions, and verify the HMAC signature on delivery as you do for all Truemed webhooks.

  1. Subscribe to dispute.created, dispute.updated, and dispute.closed.
  2. On dispute.created, store the dispute_id and surface the open dispute to your team, noting due_by.
  3. Respond before due_bysubmit evidence or accept.
  4. On dispute.updated / dispute.closed, re-fetch the dispute (or trust the payload snapshot) to track progress and the final outcome.