> This page is for version v1 (default).
> For other versions, use one of these documentation indexes:
> - v1 (default): https://developers.truemed.com/v-1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.truemed.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.truemed.com/_mcp/server.

# Dispute Closed Webhook

POST 

Truemed calls your endpoint when a dispute reaches a terminal state — a chargeback outcome (`won`, `lost`,
`accepted`, or `expired`) or a surfaced inquiry the bank drops (`closed`). A re-sync of an already-terminal dispute
does not re-fire this event, and an inquiry already terminal the first time Truemed sees it fires nothing at all.

**Authentication**: Requests are HMAC-SHA256 signed. The `x-truemed-signature` header carries a timestamp and
the signature in the form `t=<unix-timestamp>,v0=<hex-signature>`. Compute HMAC-SHA256 over
`<t>.<raw-request-body>` with your webhook signing secret and compare the hex digest to `v0`. Sign the raw body
bytes exactly as received, since re-serializing the JSON changes the bytes and breaks the signature.

## Payload

The signed body is the standard Truemed envelope:

* `webhook_delivery_id` - Delivery id (`dlv_…`). Retries of the same event repeat the same value; use it to
  deduplicate.
* `event_type` - The wire event name (`dispute.created`, `dispute.updated`, or `dispute.closed`).
* `data` - 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`. Null fields are
  omitted. Payloads carry canonical Truemed fields and the public `dispute_id` UUID only, never raw processor
  shapes. Always key your records on `dispute_id`.

(Unsigned legacy endpoints receive the `data` object as the flat top-level body, with no envelope and no
`event_type`.)

The model is **notify, then fetch**: the snapshot is complete enough to act on directly, and you can re-fetch the
dispute for authoritative state when a delivery is delayed or retried out of order.

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

## Response Notes

Return HTTP `204` with an empty body. Truemed retries failed deliveries with backoff for up to 7 days.

Reference: https://developers.truemed.com/api-reference/disputes/dispute-closed-webhook

## Request

### Payload

- `data` (DisputeDetailResource, required) — The full canonical dispute — the same shape returned by `GET /api/v1/disputes/{dispute_id}`.
- `event_type` (string, required) — The event type: `dispute.created`, `dispute.updated`, or `dispute.closed`.
- `webhook_delivery_id` (string, required) — Deterministic idempotency key for this delivery (`dlv_…`); stable across retries.

## Types

### DisputeDetailResource

- `amount` (integer, required) — The original disputed amount in cents, independent of resolution.
- `created_at` (datetime, required) — When the dispute was created.
- `currency` (string, required) — ISO currency code of the disputed amount.
- `dispute_id` (string, required) — Stable Truemed dispute ID (UUID). Use this as the dispute's identifier.
- `payment` (PaymentSummaryResource, required) — Summary of the payment the dispute was filed against.
- `processor` (enum, required) — Payment processor the dispute originated from.
  - Allowed values: `STRIPE`, `FAKE`, `EXTERNAL`
- `processor_dispute_id` (string, required) — Raw payment-processor dispute ID (e.g. Stripe `du_…`), for reconciliation.
- `reason` (enum, required) — Canonical reason the dispute was filed.
  - Allowed values: `credit_not_processed`, `duplicate`, `fraudulent`, `general`, `noncompliant`, `product_not_received`, `product_unacceptable`, `subscription_canceled`, `unrecognized`
- `state` (enum, required) — Canonical dispute state.
  - Allowed values: `inquiry`, `needs_response`, `under_review`, `won`, `lost`, `accepted`, `expired`, `closed`
- `counters` (list of CounterResource, optional, default: []) — Evidence counters for this dispute (the full chain across phases).
- `due_by` (datetime, optional, nullable) — Evidence submission deadline, if the dispute is challengeable.
- `evidence_requirements` (DisputeEvidenceRequirements, optional) — Recommended evidence for this dispute's reason, grouped by weight (compelling / supporting / other), plus Truemed's merchant narrative on how to contest.
- `metadata` (map from string to any, optional, default: {}) — Arbitrary merchant-set metadata.
- `network_reason_code` (string, optional, nullable) — Raw card-network reason code behind the dispute (e.g. Visa `10.4`), if provided.
- `outcome` (enum, optional, nullable) — Terminal outcome; null until the dispute resolves.
  - Allowed values: `won`, `lost`, `accepted`, `expired`, `closed`
- `payment_session_id` (string, optional, nullable) — The Truemed payment session this dispute belongs to. Null for disputes on non-payment-session charges.
- `phase` (enum, optional, nullable) — Which round of the dispute (inquiry, dispute, pre-arbitration, arbitration).
  - Allowed values: `inquiry`, `dispute`, `pre_arbitration`, `arbitration`
- `prior_dispute_id` (string, optional, nullable) — Truemed dispute ID (UUID) of the prior round in a pre-arbitration/re-filing chain, if any.
- `resolved_at` (datetime, optional, nullable) — When the dispute reached a terminal state.

### PaymentSummaryResource

- `order_id` (string, optional, nullable) — ID (UUID) of the Truemed payment session or order the disputed payment belongs to. For a payment session it matches `payment_session_id`. Null when the payment has no linked record.

### CounterResource

- `counter_id` (string, required) — Stable Truemed counter ID (UUID).
- `created_at` (datetime, required) — When this counter was created.
- `phase` (enum, required) — Dispute phase this counter responds to.
  - Allowed values: `inquiry`, `dispute`, `pre_arbitration`, `arbitration`
- `revision` (integer, required) — Optimistic-lock revision; echo it back on the next draft save to detect conflicts.
- `state` (enum, required) — Counter lifecycle state.
  - Allowed values: `draft`, `submitted`, `withdrawn`, `superseded`
- `updated_at` (datetime, required) — When this counter was last updated.
- `evidence_items` (list of EvidenceItem, optional, default: []) — The categorized evidence captured on this counter.
- `submitted_at` (datetime, optional, nullable) — When this counter was submitted to the processor.
- `submitted_by` (string, optional, nullable) — Email of the dashboard user who submitted this counter; null for drafts or API-key submissions.

### DisputeEvidenceRequirements

- `compelling` (list of DisputeEvidenceCategorySpec, optional, default: []) — Evidence most likely to overturn the dispute, in display order.
- `merchant_explanation` (string, optional, default: ) — Truemed's reason-specific narrative (Markdown) on how to contest this dispute. Empty when the reason has no configured guidance.
- `other` (list of DisputeEvidenceCategorySpec, optional, default: []) — Every remaining category the processor accepts, so nothing is unreachable in the form.
- `supporting` (list of DisputeEvidenceCategorySpec, optional, default: []) — Additional recommended evidence (supporting and context tiers), in display order.

### EvidenceItem

- `category` (enum, required) — Canonical evidence category; the adapter maps it to the processor's evidence fields.
  - Allowed values: `product_description`, `customer_communication`, `customer_name`, `customer_email`, `customer_purchase_ip`, `customer_signature`, `billing_address`, `receipt`, `shipping_documentation`, `shipping_tracking_number`, `service_documentation`, `service_date`, `access_activity_log`, `refund_policy`, `refund_policy_disclosure`, `refund_refusal_explanation`, `cancellation_policy`, `cancellation_policy_disclosure`, `cancellation_rebuttal`, `duplicate_charge_documentation`, `duplicate_charge_explanation`, `uncategorized`
- `file_ids` (list of string, optional, default: []) — File IDs (from the Files API) attached for this category; must be owned by you.
- `text` (string, optional, nullable) — Free-text evidence for this category.

### DisputeEvidenceCategorySpec

- `category` (enum, required) — Canonical evidence category.
  - Allowed values: `product_description`, `customer_communication`, `customer_name`, `customer_email`, `customer_purchase_ip`, `customer_signature`, `billing_address`, `receipt`, `shipping_documentation`, `shipping_tracking_number`, `service_documentation`, `service_date`, `access_activity_log`, `refund_policy`, `refund_policy_disclosure`, `refund_refusal_explanation`, `cancellation_policy`, `cancellation_policy_disclosure`, `cancellation_rebuttal`, `duplicate_charge_documentation`, `duplicate_charge_explanation`, `uncategorized`
- `description` (string, required) — Merchant-facing helper text explaining what to provide; empty when the category has none.
- `file` (boolean, required) — Whether the category accepts a file upload.
- `label` (string, required) — Merchant-facing display label for the category.
- `text` (boolean, required) — Whether the category accepts free text.