# Webhooks

Register URLs to receive signed event notifications, then inspect and replay
deliveries when debugging your endpoint. All webhook endpoints require the
`webhooks:manage` scope.

Webhooks are signed with a `Webhook-Signature` header (Stripe-compatible) using
the signing secret returned once at endpoint creation. See the
[Webhooks guide](#webhooks) for verification code.

## Event types

`enabled_events` accepts any of these types, or `"*"` for all:

`event.created`, `event.updated`, `ticket.issued`, `ticket.confirmed`,
`ticket.confirmation_failed`, `ticket.transferred`, `ticket.refunded`,
`ticket.revoked`, `ticket.upgraded`, `ticket.redeemed`,
`payment.succeeded`, `payment.failed`, `marketplace.sale.completed`,
`attendance.verified`, `payout.paid`

## Delivery format and retries

Every delivery is a `POST` with headers `Webhook-Signature`
(`t=<unix-seconds>,v1=<hex>` — HMAC-SHA256 of `<timestamp>.<raw body>` with your
`whsec_` secret), `Webhook-Id` (the event id, for deduping), and
`Content-Type: application/json`. The body envelope is:

```json
{ "id": "evt_9f8e7d...", "type": "ticket.issued", "created": 1749124800, "data": { "...": "the object" } }
```

A delivery succeeds on any `2xx` within **10 seconds**. Failures are retried on
a fixed backoff — 1m, 5m, 30m, 2h, 10h, 24h after each failed attempt (**7
attempts total**) — then marked `failed`. An endpoint that accumulates **20
consecutive failures** is automatically set to `disabled` and stops receiving
events.

---

## GET /v1/webhook_endpoints

List your registered webhook endpoints. The signing secret is never returned
here — it is shown only once, at creation.

**Scope:** `webhooks:manage`

### Example request

```bash
curl https://api.ticketconnect.example/v1/webhook_endpoints \
  -H "Authorization: Bearer sk_test_your_key_here"
```

### Example response

```json
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "664f0a1b2c3d4e5f60718293",
      "url": "https://acme.example/hooks/ticketconnect",
      "enabled_events": ["*"],
      "status": "active"
    }
  ]
}
```

---

## POST /v1/webhook_endpoints

Register a webhook endpoint. The response includes the `secret` **exactly once** —
store it to verify incoming signatures.

**Scope:** `webhooks:manage`

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | Yes | Destination URL. Must be `http(s)`; **`https` is required in production**. Must resolve to a public address — private/internal addresses are rejected. |
| `enabled_events` | string[] | No | Event types to receive. Defaults to `["*"]` (all events). |

### Example request

```bash
curl https://api.ticketconnect.example/v1/webhook_endpoints \
  -H "Authorization: Bearer sk_test_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://acme.example/hooks/ticketconnect",
    "enabled_events": ["ticket.issued", "payout.paid"]
  }'
```

### Example response

```json
{
  "id": "664f0a1b2c3d4e5f60718293",
  "url": "https://acme.example/hooks/ticketconnect",
  "enabled_events": ["ticket.issued", "payout.paid"],
  "status": "active",
  "secret": "whsec_9f8e7d6c5b4a..."
}
```

> Errors: `400 parameter_missing` (no `url`), `400 parameter_invalid`
> (non-`http(s)` URL, or plain `http` in production), `400 invalid_webhook_url`
> (the URL points at a private or internal address).

---

## DELETE /v1/webhook_endpoints/{id}

Delete a webhook endpoint so it stops receiving events.

**Scope:** `webhooks:manage`

### Path params

| Param | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | Yes | Webhook endpoint id. |

### Example request

```bash
curl https://api.ticketconnect.example/v1/webhook_endpoints/664f0a1b2c3d4e5f60718293 \
  -H "Authorization: Bearer sk_test_your_key_here" \
  -X DELETE
```

### Example response

```json
{ "id": "664f0a1b2c3d4e5f60718293", "deleted": true }
```


---

## POST /v1/webhook_endpoints/{id}/ping

Send a signed test `ping` event to the endpoint and get the attempt result
back synchronously. The ping goes through the normal delivery machinery —
same `Webhook-Signature` / `Webhook-Id` headers, same SSRF checks, same
10-second timeout — so it validates your signature verification end-to-end.
Pings are never retried in the background; a failed ping counts toward the
endpoint's auto-disable failure streak and is logged as a normal delivery.

**Scope:** `webhooks:manage`

### Path params

| Param | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | Yes | Webhook endpoint id. |

### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `message` | string | No | Text echoed in the ping's `data.message`, up to 256 characters. A default is supplied when omitted. |

### Example request

```bash
curl https://api.ticketconnect.example/v1/webhook_endpoints/664f0a1b2c3d4e5f60718293/ping \
  -H "Authorization: Bearer sk_test_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "message": "hello from staging" }'
```

### Example response

```json
{
  "id": "evt_8c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f",
  "endpoint_id": "664f0a1b2c3d4e5f60718293",
  "type": "ping",
  "delivered": true,
  "response_code": 200,
  "error": null
}
```

> `delivered` is `false` on any failure; `response_code` is your endpoint's
> HTTP status (or `null` on a network error, with `error` populated). Errors:
> `400 parameter_invalid` (`message` too long), `404 resource_missing`.
---

## GET /v1/events/deliveries

List recent webhook delivery attempts, with status and the last response code,
to debug your endpoint.

**Scope:** `webhooks:manage`

### Query params

| Param | Type | Required | Description |
| --- | --- | --- | --- |
| `limit` | integer | No | Page size, `1`–`100` (default `20`). |
| `starting_after` | string | No | Cursor — a delivery id to page after. |

### Example request

```bash
curl "https://api.ticketconnect.example/v1/events/deliveries?limit=2" \
  -H "Authorization: Bearer sk_test_your_key_here"
```

### Example response

```json
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "6650bb1c2d3e4f5a60718293",
      "event_id": "evt_9f8e7d6c5b4a43210fedcba98765432",
      "type": "ticket.issued",
      "status": "delivered",
      "attempts": 1,
      "last_response_code": 200,
      "created_at": "2026-06-05T12:30:05.000Z"
    }
  ]
}
```

---

## POST /v1/events/deliveries/{id}/replay

Re-queue a previous delivery for dispatch to your endpoint — handy after fixing
an outage on your side.

**Scope:** `webhooks:manage`

### Path params

| Param | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | Yes | Delivery id. |

### Example request

```bash
curl https://api.ticketconnect.example/v1/events/deliveries/6650bb1c2d3e4f5a60718293/replay \
  -H "Authorization: Bearer sk_test_your_key_here" \
  -X POST
```

### Example response

```json
{ "id": "6650bb1c2d3e4f5a60718293", "status": "pending", "replayed": true }
```
