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

# Webhooks

> Signed outbound events delivered through Svix.

Webhooks push events to your server as they happen, so you do not need to poll.

## Register an endpoint

In the dashboard: **Settings → API & Webhooks**. Or through the API, with an API key that has the `webhooks:write` scope:

```bash theme={null}
curl -X POST "https://api.payable.run/api/v1/developer/webhook-endpoints" \
  -H "Authorization: Bearer esk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.yourcompany.com/webhooks/payable",
    "description": "Production"
  }'
```

## Verify the signature

Deliveries are signed with [Svix](https://svix.com). Always verify before trusting a payload:

```typescript theme={null}
import { Webhook } from 'svix';

const wh = new Webhook(process.env.PAYABLE_WEBHOOK_SECRET!);
const event = wh.verify(payload, {
  'svix-id': headers['svix-id'],
  'svix-timestamp': headers['svix-timestamp'],
  'svix-signature': headers['svix-signature'],
});
```

Pass the **raw request body** as `payload`, not re-serialized JSON. Respond with a `2xx` quickly and do heavy work asynchronously; failed deliveries are retried.

Webhook endpoints require a **production** organization. While the organization is in test mode, registering an endpoint answers `403` with code `PRODUCTION_REQUIRED` and no events are sent; listing and deleting endpoints keep working.

Payable does not follow redirects. A `3xx` response from your endpoint is treated as a failed delivery and retried like any other failure, so register the final URL, not one that redirects.

## Events

| Event | When |
| - | - |
| `client.created` | A client is created |
| `debt.created` | A debt is created |
| `debt.updated` | A debt changes |
| `debt.paid` | A debt is fully paid |
| `collection.enrollment.started` | A debt enters a collection flow |
| `collection.step.executed` | A flow step ran |
| `payment.received` | A payment was received |
| `message.delivered` | A WhatsApp message was delivered |
| `voice_call.completed` | An AI collection call ended (see [AI voice calls](https://docs.payable.run/voice-calls.md)) |
| `success_fee.created` | Payable recorded its success fee on a recovered payment (success-fee billing only) |

## Payload

```json theme={null}
{
  "id": "evt_uuid",
  "type": "debt.paid",
  "live": true,
  "createdAt": "2026-03-01T12:00:00.000Z",
  "data": {
    "id": "debt_uuid",
    "clientId": "client_uuid",
    "amount": "150.00"
  }
}
```

`live` is `true` for production data and `false` for sandbox. Events can be delivered more than once, so deduplicate by `id`.

### `success_fee.created`

Sent when Payable records its success fee on a recovered payment: a debt settlement or a paid agreement installment, by any payment path, made between the first collection send and the end of the attribution window after the last one. Only organizations billed by success fee receive it. There is one fee per recovered payment, so `data.id` is stable across retries.

```json theme={null}
{
  "id": "evt_uuid",
  "type": "success_fee.created",
  "live": true,
  "createdAt": "2026-10-20T12:00:00.000Z",
  "data": {
    "id": "success_fee_uuid",
    "clientId": "client_uuid",
    "debtId": "debt_uuid",
    "sourceType": "settlement",
    "kind": "recovery",
    "recoveredCents": 30000,
    "feeCents": 3000,
    "feeBps": 1000,
    "collectionMethod": "split",
    "status": "collected"
  }
}
```

| Field | Description |
| - | - |
| `id` | Success fee id |
| `clientId` | Client that paid |
| `debtId` | Debt the fee belongs to. `null` when `sourceType` is `installment` |
| `sourceType` | `settlement` (debt paid off), `installment` (agreement installment paid) or `deletion_review` (debt deleted after the client engaged, approved by Payable) |
| `kind` | `billing_fixed` (fixed fee per paid charge in Cobrança, `feeBps` = 0) or `recovery` (percentage of what was recovered in Recuperação, and every fee recorded before the new pricing launched). See [Recuperação](https://docs.payable.run/recovery.md) |
| `recoveredCents` | Amount the fee applies to, in cents. On a settlement it excludes what was already charged on installments of the same debt |
| `feeCents` | Fee in cents. For `recovery`, `recoveredCents × feeBps / 10000`, rounded half up; for `billing_fixed`, the fixed amount, never more than what was paid |
| `feeBps` | Fee rate in basis points (`1000` = 10%) |
| `collectionMethod` | How the fee is collected: `split` (withheld from the payment itself), `custody` (withheld from the payout), `wallet` (debited from your Payable balance) or `none` |
| `status` | Fee status, e.g. `collected`, `charged`, `pending_custody`, `withheld`, `waived` or `review` |


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