> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rubicourt.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Signed events for every dispute and case, retried until you accept them.

Rubicourt POSTs JSON to your webhook URL:

```json theme={null}
{
  "id": "cmw...",
  "type": "dispute.decided",
  "created_at": "2026-11-01T22:59:11.574Z",
  "data": { "dispute_id": "...", "reference": "RD-2026-000001", "external_reference": "order-9001", "status": "decided", "...": "..." }
}
```

| Events | |
| - | - |
| Resolve | `dispute.opened`, `dispute.needs_review`, `dispute.decided`, `dispute.arbitration_requested`, `dispute.escalated`, `dispute.closed` |
| Arbitration | `case.created`, `case.stage_changed`, `case.award_issued`, `case.appeal_filed`, `case.closed`, `case.award_paid` |

## Verify the signature

Each request carries `Rubicourt-Signature: t=<unix seconds>,v1=<hex>`. The `v1` value is an HMAC-SHA256 of `<t>.<raw body>` with your webhook secret. Reject requests older than five minutes.

```ts Node theme={null}
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(rawBody: string, header: string, secret: string) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
  const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ""));
}
```

```python Python theme={null}
import hashlib, hmac, time

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    if abs(time.time() - int(parts["t"])) > 300:
        return False
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))
```

## Delivery

* Respond with any `2xx` within 10 seconds.
* Failed deliveries retry after 1 minute, 10 minutes, 1 hour and 6 hours.
* Use the event `id` to ignore duplicates.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.