# Add a Webhook Endpoint

One more address for your tenant's events in one environment, with the event types it asks for. Its secret is shown in this answer, and never again.

## Overview

- **`201` with `endpoint` and `secret`.** Store the secret now: it signs every delivery to this endpoint ([Verify the Signature](https://docs.aureahub.com/docs/guide-events.md)). A new one: [Give an Endpoint a New Secret](https://docs.aureahub.com/docs/webhook-endpoint-rotate.md).
- `url`: `https://` on the default port with a full public DNS name — the same rules as the webhook of each environment, listed in [Register Your Server](https://docs.aureahub.com/docs/guide-events.md).
- `eventTypes`: the types it receives, any of those [List Endpoints](https://docs.aureahub.com/docs/webhook-endpoints-list.md) answers. `[]` receives the five `ramp.*` types only: every other type — `earn.*`, `operation.*`, `transaction.*`, `wallet.*`, `aml_case.*`… — and every type published later reach the endpoint only when it names them ([Event Types](https://docs.aureahub.com/docs/guide-events.md)). A type named twice counts once; the answer lists them in the catalogue's order.
- `mtls`, where your tenant has mutual TLS on deliveries [Coming soon, upon request](https://docs.aureahub.com/docs/feature-availability.md): `true` has its deliveries present Aurea's client certificate ([Mutual TLS](https://docs.aureahub.com/docs/guide-events.md)); the default is `false`. Otherwise `true` answers `409` `WEBHOOK_MTLS_UNAVAILABLE` and nothing is saved.
- `description`: your own label, up to 200 characters without control characters, trimmed; `null` or empty: none. `status`: `active` (the default) or `disabled`.
- At most 10 endpoints per environment: an eleventh answers `409` `WEBHOOK_ENDPOINT_LIMIT`. Two requests racing for the tenth place: one of them gets it.
- The body is checked as sent: a field that doesn't belong, or a string where a list goes, is refused rather than ignored or converted.
- For a tenant role that manages the webhooks — `owner`, `tenantadmin`, `non_signing_admin` or `security_admin` ([Roles](https://docs.aureahub.com/docs/authentication.md)), on its own tenant. Written to the tenant's activity log, never with the secret.

> Note: **Your tenant's quorum** [Coming soon, upon request](https://docs.aureahub.com/docs/feature-availability.md) — where it holds administrators' actions (a tenant on the institutional security profile by its profile; every other tenant's records them only): this call answers `202` `{ "state": "awaiting_quorum", "requestId" }` — the **202** tab below — and nothing is done until another administrator of your tenant approves it; the answer it would have given is then read once by who asked ([Read the Answer](https://docs.aureahub.com/docs/quorum-request-result.md)). With no other administrator able to approve it, it is done at once. [Your Tenant's Quorum](https://docs.aureahub.com/docs/guide-tenant-quorum.md). Every other tenant gets the answers below, unchanged.

## Endpoint

### `POST /v1/admin/tenants/{id}/webhook-endpoints`

Authentication: bearer token required.

Adds a webhook endpoint in one environment and returns its secret once.

**Path parameters**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | string | yes | Tenant UUID |

**Request body**

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `environment` | string | yes | `production` or `sandbox` |
| `url` | string | yes | Your server's address, up to 2048 characters |
| `eventTypes` | string[] | yes | The types it receives, any of the published types ([Webhooks & Events › Event Types](https://docs.aureahub.com/docs/guide-events.md)); empty: the five `ramp.*` types only |
| `description` | string \| null | no | Your label, up to 200 characters |
| `status` | string | no | `active` (default) or `disabled` |
| `mtls` | boolean | no | Where your tenant has mutual TLS (on request): deliveries present Aurea's client certificate. Default `false`; otherwise `true` answers `409` `WEBHOOK_MTLS_UNAVAILABLE` |

**Responses**

`201` Added

```json
{
  "endpoint": {
    "id": "8a3c5e71-2b4d-4f60-9e1a-7c2d4b6f8e01",
    "environment": "production",
    "url": "https://ledger.example.com/aurea/events",
    "description": "Ledger service",
    "eventTypes": ["ramp.deposit.updated", "ramp.transaction.updated"],
    "status": "active",
    "secretHint": "Zt4w",
    "secretRotatedAt": "2026-10-06T08:12:40.511Z",
    "createdAt": "2026-10-06T08:12:40.511Z",
    "updatedAt": "2026-10-06T08:12:40.511Z"
  },
  "secret": "whsec_… (44 characters of base64, ending Zt4w=)"
}
```

`202` Awaiting quorum

```json
{
  "state": "awaiting_quorum",
  "requestId": "4e2b9c17-8a3d-4f60-b5e1-7c9d0a2f6b38"
}
```

`400` Refused

```json
{
  "statusCode": 400,
  "error": "BadRequestError",
  "message": "The webhook URL must use https",
  "details": { "code": "WEBHOOK_ENDPOINT_INVALID", "field": "url", "reason": "not_https" }
}
```

`409` Eleventh

```json
{
  "statusCode": 409,
  "error": "ConflictError",
  "message": "A tenant has at most 10 webhook endpoints in production",
  "details": { "code": "WEBHOOK_ENDPOINT_LIMIT" }
}
```

`409` No mutual TLS

```json
{
  "statusCode": 409,
  "error": "ConflictError",
  "message": "Aurea does not offer mutual TLS on webhook deliveries at the moment",
  "details": { "code": "WEBHOOK_MTLS_UNAVAILABLE" }
}
```

**Example request**

```bash
curl -X POST https://api.aureahub.com/v1/admin/tenants/$TENANT_ID/webhook-endpoints \
  -H "Authorization: Bearer $TENANT_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "environment": "sandbox", "url": "https://ledger.example.com/aurea/events", "eventTypes": ["ramp.deposit.updated"], "description": "Ledger service" }'
```

## Refusals

Nothing is saved, and the answer is `400` with `details.code` `WEBHOOK_ENDPOINT_INVALID`, the field at fault in `details.field` and, where the field's type is wrong, each problem in `details.issues`:

- `url` an address Aurea won't call, with `details.reason` — `not_https`, `port`, `ip_address`, `local_host`, `host_name`, `credentials`, `fragment`, `whitespace_or_control`, `backslash`, `dot_segment` or `not_a_url`, as for the [webhook of each environment](https://docs.aureahub.com/docs/tenant-webhook-save.md).
- `eventTypes`: not a list, more than fifty-one entries, or a type that is not published (`webhook.test` included: only a test sends it).
- `mtls`: not `true` or `false`.
- `environment`: neither `production` nor `sandbox` (`The environment is production or sandbox`).
- `description`: longer than 200 characters, or with a control character. `status`: neither `active` nor `disabled`.
- `body`: a field that doesn't belong, for example `secret` — the API always makes the secret (`Unrecognized key(s) in object: 'secret'`).
- `id`: a tenant id that is not a UUID (`The tenant id must be a UUID`).

---

Web version: https://docs.aureahub.com/#webhook-endpoint-add
