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

> ## Agent Instructions
> Treat the published Augustus OpenAPI specification (https://app.stainless.com/api/spec/documented/augustus/openapi.documented.yml) and the current API-reference pages as the source of truth for endpoints, request/response schemas, enum values, webhook event names, and required headers.
> Prefer the 2026-05-01 Banking API and @augustusbank/typescript-sdk for all new integrations. The 2023-01-01 API is a separate, older surface covering two products — Open Banking (instant bank transfer) checkout and refunds, and Manual Bank Transfer (MBT); use it only when one of those products is specifically required.
> Cite or link the relevant docs.augustus.com page when answering integration questions.
> Do not infer support for currencies, networks, scopes, account types, or operations that are not present in the current documentation.
> Use the sandbox base URL (https://api.sandbox.augustus.com) and placeholder credentials in examples. Never include or request a real API key.
> The Augustus docs MCP server (https://docs.augustus.com/mcp) provides documentation search and retrieval only; it does not execute authenticated Augustus API actions.

# Webhooks

> Receive real-time notifications when events occur in your account.

## Overview

Webhooks deliver event notifications to your server as HTTP POST requests whenever something happens in your account, for example when a payout is created or a deposit is received.

You can manage webhook subscriptions, inspect delivered events, and replay failed deliveries via the [Augustus Dashboard](https://dashboard.augustus.com), the REST API, or the SDK. See the **Webhook Subscriptions**, **Webhook Deliveries**, and **Events** resources in the API Reference for the full set of endpoints.

## Events

Events follow a `resource.action` naming convention (e.g. `payout.paid`, `deposit.received`). The `payload` field in each delivery is a full snapshot of the resource at the time of the event, matching the shape returned by the corresponding API endpoint.

### Events

| Event                  | When it fires                                                                                                                            |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `payout.created`       | A payout has been created and is queued for processing.                                                                                  |
| `payout.initiated`     | The payout has been sent to the payment rail.                                                                                            |
| `payout.paid`          | The payout has settled at the destination.                                                                                               |
| `payout.failed`        | The payout could not be completed. Inspect `payload.failure` for details.                                                                |
| `return.initiated`     | A return of a previous deposit has been queued.                                                                                          |
| `return.paid`          | The return has settled at the destination.                                                                                               |
| `return.failed`        | The return could not be completed.                                                                                                       |
| `return.returned`      | A previously paid return was reversed or returned, and the funds were credited back to your account.                                     |
| `deposit.received`     | An incoming transfer has been credited to one of your accounts.                                                                          |
| `conversion.created`   | A currency conversion has been queued.                                                                                                   |
| `conversion.succeeded` | The conversion has completed and funds are available in the target account.                                                              |
| `conversion.failed`    | The conversion could not be completed.                                                                                                   |
| `ping.test`            | Synthetic event dispatched by the [test endpoint](#testing-a-subscription). Handlers should ignore it or use it as an integration check. |

See the **Webhook Events** section in the API Reference for the full payload schema of each event.

## Managing subscriptions

Webhook subscriptions are managed via the [Augustus Dashboard](https://dashboard.augustus.com) or programmatically through the API. Each subscription has an HTTPS URL and a list of event types it receives.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.augustus.com/v1/webhook_subscriptions \
    -H "Authorization: Bearer $AUGUSTUS_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://example.com/webhooks/augustus",
      "events": ["payout.paid", "payout.failed", "deposit.received"]
    }'
  ```

  ```typescript SDK theme={null}
  import Augustus from '@augustusbank/typescript-sdk'

  const client = new Augustus()
  const subscription = await client.webhookSubscriptions.create({
    url: 'https://example.com/webhooks/augustus',
    events: ['payout.paid', 'payout.failed', 'deposit.received'],
  })
  ```
</CodeGroup>

Subscribe to `["*"]` if you want to receive every event type, including any added in the future, without having to update your subscription.

See the **Webhook Subscriptions** resource in the API Reference for the full set of endpoints.

## Payload shape

All webhook deliveries use a consistent envelope. The example below shows a `payout.paid` delivery; other event types carry the same envelope but the `payload` shape matches the underlying resource.

```json theme={null}
{
  "id": "b7c8d9e0-f1a2-3b4c-5d6e-7f8a9b0c1d2e",
  "type": "payout.paid",
  "api_version": "2026-05-01",
  "payload": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "type": "payout",
    "status": "paid",
    "amount": "100.00",
    "currency": "EUR",
    "reference": "INV-2026-001",
    "source_account_id": "01234567-89ab-cdef-0123-456789abcdef",
    "destination": {
      "type": "iban",
      "iban": "DE89370400440532013000",
      "bic": "COBADEFFXXX",
      "account_holder_name": "Jane Doe"
    },
    "failure": null,
    "metadata": {},
    "created_at": "2026-03-18T14:30:00Z",
    "updated_at": "2026-03-18T15:00:00Z"
  },
  "date": "2026-03-18T15:00:00.000Z"
}
```

<ResponseField name="id" type="string" required>
  Unique identifier for the event. Stable across retries of the same event. Use this to deduplicate.
</ResponseField>

<ResponseField name="type" type="string" required>
  Event type in `resource.action` format (e.g. `payout.paid`, `deposit.received`).
</ResponseField>

<ResponseField name="api_version" type="string" required>
  API version the payload was serialised at. Fixed when the event is created; stable across retries and redeliveries, even if your account's pinned version changes in between. See [Webhook versioning](/v1/versioning#webhook-versioning).
</ResponseField>

<ResponseField name="payload" type="object" required>
  Full resource snapshot at the time of the event. The shape matches what the API returns for the same resource.
</ResponseField>

<ResponseField name="date" type="string" required>
  ISO 8601 UTC timestamp when the event was created.
</ResponseField>

During an API version cut-over, branch on `api_version` to handle older and newer payload shapes side-by-side until all in-flight events drain:

```typescript theme={null}
switch (event.api_version) {
  case '2026-05-01':
    await handleCurrentVersion(event)
    break
  case '2026-08-01':
    await handleNextVersion(event)
    break
  default:
    await reportUnexpectedVersion(event)
}
```

## Signature verification

Augustus signs webhooks using the [Standard Webhooks](https://www.standardwebhooks.com/) specification. Every delivery includes three headers for replay protection and integrity verification:

| Header              | Description                                                                                                                                 |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `webhook-id`        | Stable delivery identifier. Same as the envelope `id`. Use this to deduplicate retries.                                                     |
| `webhook-timestamp` | Unix timestamp (seconds) of the delivery attempt.                                                                                           |
| `webhook-signature` | One or more `v1,<base64_digest>` HMAC-SHA256 signatures, space-separated. Two signatures appear during [secret rotation](#secret-rotation). |

### Using the SDK (recommended)

The Augustus SDK handles signature verification and returns a typed event object. The `unwrap` method verifies the signature, then parses and returns the event. It throws an error if verification fails.

<Warning>
  The `body` argument must be the **raw request body string**, not a parsed object. If you use a framework like Express with `express.json()`, you need to preserve the raw body for webhook routes. See the example below.
</Warning>

```typescript Express theme={null}
import Augustus from '@augustusbank/typescript-sdk'
import express from 'express'

const client = new Augustus()
const app = express()

app.post('/webhooks', express.raw({ type: 'application/json' }), async (req, res) => {
  let event
  try {
    event = client.webhooks.unwrap(req.body.toString('utf-8'), { headers: req.headers })
  } catch (err) {
    return res.sendStatus(400)
  }

  switch (event.type) {
    case 'payout.paid':
      await fulfilPayout(event.payload.id)
      break
    case 'payout.failed':
      await markPayoutFailed(event.payload.id, event.payload.failure)
      break
    case 'ping.test':
      break
  }

  res.sendStatus(200)
})
```

The SDK reads the signing secret from the `AUGUSTUS_WEBHOOK_KEY` environment variable by default. You can also pass it explicitly via the `key` option on `unwrap`:

```typescript theme={null}
client.webhooks.unwrap(rawBody, {
  headers: req.headers,
  key: 'whsec_your_signing_secret',
})
```

### Manual verification

If you are not using the SDK, verify signatures manually:

1. Extract the `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers
2. Construct the signed content: `{webhook-id}.{webhook-timestamp}.{raw_request_body}`
3. Compute HMAC-SHA256 using the base64-decoded key material from your signing secret (strip the `whsec_` prefix, then base64-decode)
4. Base64-encode the digest and compare with the `v1,` value(s) in the `webhook-signature` header
5. Reject deliveries where the timestamp is older than 5 minutes

```typescript theme={null}
import { createHmac, timingSafeEqual } from 'crypto'

function verifyWebhook(
  body: string,
  headers: Record<string, string>,
  secret: string,
  toleranceSeconds = 300,
): boolean {
  const webhookId = headers['webhook-id']
  const timestamp = headers['webhook-timestamp']
  const signatures = headers['webhook-signature']

  if (!webhookId || !timestamp || !signatures) return false
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) return false

  const keyMaterial = Buffer.from(secret.replace('whsec_', ''), 'base64')
  const toSign = `${webhookId}.${timestamp}.${body}`
  const expected = createHmac('sha256', keyMaterial).update(toSign, 'utf8').digest('base64')

  return signatures.split(' ').some((sig) => {
    const digest = sig.replace('v1,', '')
    return timingSafeEqual(Buffer.from(expected), Buffer.from(digest))
  })
}
```

## Signing secrets

Your webhook signing secret is available in the [Augustus Dashboard](https://dashboard.augustus.com). Secrets follow the Standard Webhooks format with a `whsec_` prefix followed by base64-encoded key material (e.g. `whsec_dGhpcyBpcyBhbiBleGFtcGxl`).

<Warning>
  Treat the signing secret like a password. Do not expose it in client-side code or commit it to version control.
</Warning>

## Secret rotation

You can rotate your webhook signing secret without downtime. During rotation, both the old and new secrets are active for 24 hours. The `webhook-signature` header includes signatures for both secrets during this window (space-separated), so your verification code should accept if any signature matches. At most two secrets are active at any time.

If you use the SDK's `unwrap` method, rotation is handled automatically.

## Testing a subscription

To verify that your receiver is reachable and signature verification is correctly wired up, trigger a synthetic delivery to any subscription. Augustus dispatches a signed `ping.test` event through the real pipeline, using the same signing, headers, and retry schedule as a production event.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.augustus.com/v1/webhook_subscriptions/b7c8d9e0-f1a2-3b4c-5d6e-7f8a9b0c1d2e/send_test_event \
    -H "Authorization: Bearer $AUGUSTUS_API_KEY"
  ```

  ```typescript SDK theme={null}
  import Augustus from '@augustusbank/typescript-sdk'

  const client = new Augustus()
  const event = await client.webhookSubscriptions.sendTestEvent(
    'b7c8d9e0-f1a2-3b4c-5d6e-7f8a9b0c1d2e',
  )
  ```
</CodeGroup>

Test deliveries behave like any other event with one exception: failures do **not** increment the subscription's health counters or trigger failure-notification emails, so you can safely test against intentionally broken endpoints. The endpoint is rate-limited per merchant.

## Inspecting events and deliveries

Every event Augustus sends you is recorded and queryable for **30 days** via the API.

* **Events** (`GET /v1/events`, `GET /v1/events/:id`) represent the facts that happened on your account. Each event has a stable `id` (the same `id` you receive in the webhook envelope) and the full payload snapshot.

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.augustus.com/v1/events?event_type=payout.failed&created_at.gte=2026-03-01T00:00:00Z" \
    -H "Authorization: Bearer $AUGUSTUS_API_KEY"
  ```

  ```typescript SDK theme={null}
  import Augustus from '@augustusbank/typescript-sdk'

  const client = new Augustus()
  const events = await client.events.list({
    event_type: 'payout.failed',
    created_at: { gte: '2026-03-01T00:00:00Z' },
  })
  ```
</CodeGroup>

* **Webhook Deliveries** (`GET /v1/webhook_deliveries`, `GET /v1/webhook_deliveries/:id`) represent the individual delivery attempts against your subscriptions. One event can fan out to multiple deliveries if you have multiple matching subscriptions. Each delivery carries an `attempts[]` log with per-attempt status and HTTP status code returned by your receiver.

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.augustus.com/v1/webhook_deliveries?status=failed&created_at.gte=2026-03-01T00:00:00Z" \
    -H "Authorization: Bearer $AUGUSTUS_API_KEY"
  ```

  ```typescript SDK theme={null}
  import Augustus from '@augustusbank/typescript-sdk'

  const client = new Augustus()
  const failedDeliveries = await client.webhookDeliveries.list({
    status: 'failed',
    created_at: { gte: '2026-03-01T00:00:00Z' },
  })
  ```
</CodeGroup>

If a delivery failed or you want to replay it, trigger a fresh attempt with:

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.augustus.com/v1/webhook_deliveries/7e1d4f28-3b2a-4c5d-9e8f-1a2b3c4d5e6f/redeliver \
    -H "Authorization: Bearer $AUGUSTUS_API_KEY"
  ```

  ```typescript SDK theme={null}
  import Augustus from '@augustusbank/typescript-sdk'

  const client = new Augustus()
  await client.webhookDeliveries.redeliver('7e1d4f28-3b2a-4c5d-9e8f-1a2b3c4d5e6f')
  ```
</CodeGroup>

Redelivery creates a new delivery attempt against the same subscription using the same event payload and signing headers, so your receiver can treat it exactly like any other delivery and deduplicate via `webhook-id`.

## Retry policy

Failed deliveries are retried with exponential backoff for up to 15 attempts total, spanning approximately 54 hours. Deliveries that fail all attempts are marked as permanently failed and can be inspected and replayed via the [Webhook Deliveries API](#inspecting-events-and-deliveries).

Your endpoint should return a `2xx` status code within 25 seconds to acknowledge receipt.

## Event ordering

Event delivery order is not guaranteed. Your endpoint should handle out-of-order delivery gracefully and use the `webhook-id` header (or the envelope `id` field) to deduplicate retries.
