# Claim Messages

Use one webhook for the whole claim. File the claim with `POST /claims`, then handle the events below. The same events are available on every account. Follow-up text is also emailed. The webhook is how your system receives it.

Store each event by its `id`. EasyPost may deliver the same event more than once.

Related:

- [Events](https://docs.easypost.com/docs/insurance/claims)

---

## Events

`description` is the event name. `result` is the payload.

- **`claim.message`**, `direction` `outbound`. Show `body` and any attachments. Collect the reply in your system.
- **`claim.message`**, `direction` `inbound`. A customer reply, from this API or from email. Use the message `id` to recognize one you already stored.
- **`claim.approved`**. Show `approved_amount`. If `status` is `approved_partial`, that amount is all that will be paid.
- **`claim.rejected`**. Show `status_detail`. That sentence is the denial reason.
- **`claim.submitted`**, **`claim.updated`**, and **`claim.cancelled`**. Update the claim from `status`.

On `claim.message`, `result` is a message (`cmsg_...`). On the other events, `result` is the Claim (`clm_...`).

### A question from EasyPost

<NoSSRCodeBlock
  header="Response"
  headerColor="neutral"
  codes={[
    {
      language: "json",
      maxLines: 16,
      code: `{
  "description": "claim.message",
  "result": {
    "id": "cmsg_8f0c1c2e4b6a4d0a9c1e2f3a4b5c6d7e",
    "object": "ClaimMessage",
    "mode": "production",
    "claim_id": "clm_09d89e88a0c943d59018bc26b041c41c",
    "direction": "outbound",
    "body": "This ticket is being handled by our Automated Insurance Claim Assistant.\\n\\nSend proof of purchase that shows the completed purchase, line items, and total — an invoice, receipt, checkout thank-you screenshot, or order-confirmation email. This is required; the claim may be rejected if you cannot provide it:\\n\\n- the purchase invoice or receipt — so we can confirm the item and its value — this is required, and the claim may be rejected without it (for example, a checkout thank-you screenshot or order confirmation email that shows the completed purchase, line items, and total)",
    "attachments": [],
    "created_at": "2026-09-26T17:04:11Z",
    "updated_at": "2026-09-26T17:04:11Z"
  }
}`,
    },
  ]}
/>

### A denial

Show `status_detail` as written. When EasyPost knows what was missing or unclear, the sentence names it.

<NoSSRCodeBlock
  header="Response"
  headerColor="neutral"
  codes={[
    {
      language: "json",
      code: `{
  "description": "claim.rejected",
  "result": {
    "id": "clm_09d89e88a0c943d59018bc26b041c41c",
    "object": "Claim",
    "status": "rejected",
    "status_detail": "We could not approve this claim because we did not receive a purchase invoice or receipt that shows the item and its value.",
    "approved_amount": null
  }
}`,
    },
  ]}
/>

The same sentence names other gaps. For example: "We could not approve this claim because the damage photo did not show the item clearly enough to review." When EasyPost does not know the gap, `status_detail` is "Insufficient evidence", "Item not covered under policy", "Claim filed outside the allowed window", "Required information was not provided", or "Claim does not meet policy requirements".

`GET /claims/:id` returns the same status and reason if you missed the webhook.

---

## Send the reply

`POST /claims/:id/messages`

Send the seller's text in `body` and files in `attachments` as base64 images or PDFs, the same way you attach files when creating a claim. Include `idempotency_key`, up to 64 ASCII characters, so a retry does not file the reply twice. A retry sent while the first request is still being delivered returns `409`; send it again with the same key.

<NoSSRCodeBlock
  header={<CodeBlockEndpointHeader>POST /claims/:id/messages</CodeBlockEndpointHeader>}
  codes={[
    {
      language: "shell",
      code: `curl -X POST https://api.easypost.com/v2/claims/clm_.../messages \\
  -u "EASYPOST_API_KEY": \\
  -H "Content-Type: application/json" \\
  -d '{
    "body": "Here is the order confirmation showing the item and the total.",
    "attachments": ["REPLACE_WITH_BASE64_STRING"],
    "idempotency_key": "order-confirmation-1"
  }'`,
    },
  ]}
/>

EasyPost adds that reply to the claim, including when a person is handling it. The next question arrives as another `claim.message` event.

`GET /claims/:id/messages` lists the thread oldest first if you missed
a webhook. Use `before_id` or `after_id`, not both.

A test API key stores the reply and sends a test-mode event. In production, retry if the claim is not ready to receive messages yet. A cancelled claim cannot take a reply.