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

# OAuth 2.0 for Webhooks

> Configure Fireblocks to obtain a bearer token from your authorization server and attach it to every webhook delivery.

Your receiver may require a valid bearer token from your own authorization server on every request. With OAuth, Fireblocks gets a token from that server and sends it with every delivery, so Fireblocks authenticates the same way as any other caller and nothing in your receiver's rules has to change.

Before each delivery, Fireblocks uses a cached token, or requests a new one from your authorization server when needed. Fireblocks obtains tokens with the OAuth 2.0 client credentials grant and sends them as `Authorization: Bearer <your-access-token>`. Each OAuth record has its own token cache, so your authorization server sees far fewer token requests than deliveries.

Credentials live in one object, an OAuth record. Create it once, attach it to as many webhooks as you need, and rotate the secret in a single call.

> [!NOTE]
>
> **Availability:** OAuth 2.0 for webhooks is available to Enterprise customers and above. Contact your Customer Success Manager to enable it for your workspace.

## How it works

* Your authorization server issues access tokens. Fireblocks calls it the **token endpoint**.
* Your **receiver** is the endpoint the webhook is delivered to, and the thing that wants a bearer token.
* An **OAuth record** holds the client ID, secret, and token endpoint. Create it on its own, then point a webhook at it with `webhookOauthId`.

A webhook can be detached from an OAuth record without deleting either. A webhook with no OAuth record delivers without an `Authorization` header.

## Before you start

Check that each of these holds, because OAuth can't work for your receiver until they do:

* **OAuth is enabled for your workspace.** Ask your Customer Success Manager.
* **Your token endpoint is public.** It must use HTTPS on port 443, with a hostname that resolves in public DNS to a public address, so an endpoint on a private network or behind a VPN can't be reached.
* **Your authorization server supports the client credentials grant.** It's the only grant Fireblocks uses.
* **Your client uses a supported authentication method,** which is `client_secret_basic`, `client_secret_post`, or `client_secret_jwt`. Assertions signed with an asymmetric key (`private_key_jwt`) aren't supported yet.
* **Your tokens are valid for more than 15 seconds.** Fireblocks recommends 5 minutes. See [Token lifetime](#token-lifetime).

If your token endpoint requires a client certificate, you also need webhook mTLS enabled for your workspace, which is a separate switch from OAuth, and an [mTLS record](https://developers.fireblocks.com/reference/webhooks-mtls.md) to reference from the OAuth record. See [Optional token request parameters](#optional-token-request-parameters).

## Choose an authentication method

Set `authMethod` to match how your client is registered on your authorization server. It defaults to `client_secret_basic`, and a mismatch returns `401` from your authorization server. Each method sends the same form-encoded POST with `grant_type=client_credentials`, and places the credentials differently.

`client_secret_basic` sends the credentials in an HTTP Basic header. Use it when your authorization server expects basic authentication:

```http theme={"system"}
Authorization: Basic base64(<your-client-id>:<your-client-secret>)

grant_type=client_credentials
```

`client_secret_post` sends them in the form body. Use it when your authorization server expects `client_id` and `client_secret` as body parameters:

```http theme={"system"}
grant_type=client_credentials&client_id=<your-client-id>&client_secret=<your-client-secret>
```

`client_secret_jwt` sends a short-lived JWT signed with your client secret, so the secret itself is never transmitted. Use it when your authorization server expects a client assertion:

```http theme={"system"}
grant_type=client_credentials&client_id=<your-client-id>
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
&client_assertion=<your-signed-jwt>
```

For an authorization server that expects a different method, such as an assertion signed with an asymmetric key, contact your Customer Success Manager.

## Quick start

### 1. Create an OAuth record

Call [Create OAuth credentials](https://developers.fireblocks.com/api-reference/webhooks-v2/create-oauth-credentials.md):

```json theme={"system"}
{
  "name": "Production receiver credentials",
  "clientId": "<your-client-id>",
  "clientSecret": "<your-client-secret>",
  "url": "https://auth.example.com/oauth2/token",
  "authMethod": "client_secret_basic"
}
```

The response returns the OAuth record without the secret. Keep the `id`, because you attach it to a webhook next. The endpoint accepts an optional `Idempotency-Key` header if you want a retried create to be safe.

### 2. Attach it to a webhook

Set `webhookOauthId` on [Create a new webhook](https://developers.fireblocks.com/api-reference/webhooks-v2/create-a-new-webhook.md), or on an existing one with [Update webhook](https://developers.fireblocks.com/api-reference/webhooks-v2/update-webhook.md):

```json theme={"system"}
{ "webhookOauthId": "a1b2c3d4-1234-5678-9abc-def012345678" }
```

Attach the same `webhookOauthId` to as many webhooks as you need. To stop sending a bearer token on one webhook without affecting the others, set `webhookOauthId` to `null` on that webhook.

A webhook with an OAuth record attached can't also have an `authorization` [custom header](https://developers.fireblocks.com/reference/webhooks-custom-headers.md), because Fireblocks sets that header itself from the record, so a request that would leave both in place is rejected. One request can remove the header and attach the record together, so switching from a static credential to OAuth needs no intermediate step.

### 3. What your receiver sees

Deliveries for that webhook now carry one extra header:

```http theme={"system"}
Authorization: Bearer <your-access-token>
```

## Optional token request parameters

Four optional fields extend the token request beyond what [Create OAuth credentials](https://developers.fireblocks.com/api-reference/webhooks-v2/create-oauth-credentials.md) requires:

* `customBodyParams` adds parameters to the token request body with every `authMethod`, such as a `scope`, `audience`, or `resource` your authorization server expects.
* `customJwtClaims` adds claims inside the signed assertion and is read only by `client_secret_jwt`. Fireblocks sets `aud`, `iss`, `sub`, `jti`, `iat` and `exp` in every assertion. You can override `aud`; otherwise it is your token endpoint URL. The other five can't be overridden, and setting one is refused with `400`.
* `customHeaders` adds a static header your authorization server's gateway requires. It applies to the token request only, so for headers on the webhook delivery itself use the webhook's own [custom headers](https://developers.fireblocks.com/reference/webhooks-custom-headers.md). Header names are stored and sent lowercased. You can set `authorization` here only with `client_secret_post` or `client_secret_jwt`, because with `client_secret_basic` the credentials are that header.
* `webhookMtlsId` points the token request at an mTLS record, so Fireblocks presents that record's client certificate to your authorization server. It can be the same mTLS record your webhook uses, and it requires webhook mTLS to be enabled for your workspace. See [mTLS on the token request](https://developers.fireblocks.com/reference/webhooks-mtls.md#mtls-on-the-token-request).

See [Create OAuth credentials](https://developers.fireblocks.com/api-reference/webhooks-v2/create-oauth-credentials.md) for the reserved names and size limits of each field.

## Your authorization server

### Token lifetime

Your authorization server has to issue tokens valid for more than 15 seconds. A webhook delivery times out after 15 seconds, so a token with less time left could lapse mid-request while your endpoint is still processing the delivery. Fireblocks doesn't use a token with less than that remaining, so a shorter lifetime fails every delivery without a retry. The failure doesn't name the lifetime as the cause, and from your authorization server's side the token request succeeded.

Fireblocks recommends a token lifetime of 5 minutes. The 15-second floor is not a target, because a lifetime near it means a token request on almost every delivery and no margin for a slow one.

### Allowlisting Fireblocks

Token requests are sent from the same IP addresses as webhook deliveries, so your existing [webhook IP allowlist](https://developers.fireblocks.com/reference/webhooks-ip-allowlisting.md) covers your authorization server too.

## Manage OAuth records

| Operation | Endpoint |
| - | - |
| List all | [Get all OAuth credentials](https://developers.fireblocks.com/api-reference/webhooks-v2/get-all-oauth-credentials.md) |
| Read one | [Get OAuth credentials by id](https://developers.fireblocks.com/api-reference/webhooks-v2/get-oauth-credentials-by-id.md) |
| Update | [Update OAuth credentials](https://developers.fireblocks.com/api-reference/webhooks-v2/update-oauth-credentials.md) |
| Delete | [Delete OAuth credentials](https://developers.fireblocks.com/api-reference/webhooks-v2/delete-oauth-credentials.md) |

Reads return every field except `clientSecret`, and names only for the three maps. Keep your own record of the values you set.

Creating, updating, and deleting an OAuth record requires the Owner, Admin, or Non-Signing Admin role. Reading is open to every role, so keep secrets out of key names, because any member of the workspace can see client IDs, token endpoint URLs, and custom key names.

Update is a partial change, so a field you omit is left as stored. `name`, `clientId`, `clientSecret`, `url` and `authMethod` can't be cleared, so sending one of them as `null` is refused with `400`. The three maps merge key by key, which means a key with a value is added or updated, a key sent as `null` is removed, and the whole field sent as `null` clears the map. Rotate the secret by sending only the new one, and the next delivery for every webhook attached to the OAuth record uses it:

```json theme={"system"}
{ "clientSecret": "<your-new-client-secret>" }
```

Deleting an OAuth record that's still referenced by a webhook is refused with `409 Conflict`, and the response names those webhooks. Detach them first, or pass `forceDelete=true` to detach them and delete the record in one call. The response lists the detached webhook IDs, and those webhooks keep delivering without an `Authorization` header.

## Troubleshooting

| Symptom | Likely cause |
| - | - |
| A webhook's deliveries carry no token, and the OAuth record looks correct | `webhookOauthId` wasn't set on the webhook, or was cleared by a later update. Check that it appears on the webhook |
| Deliveries carry no token, and `webhookOauthId` is set | OAuth isn't enabled for your workspace. Deliveries still go out untokened, and nothing flags it except your receiver rejecting the request. Contact your Customer Success Manager |
| Setting `webhookOauthId` is rejected | The webhook has an `authorization` custom header. Remove the header in the same request |
| Creating an OAuth record is rejected over an `authorization` token request header | `authMethod` is `client_secret_basic`, which sends the credentials in that header itself. Remove the header, or switch to `client_secret_post` or `client_secret_jwt` |
| Creating an OAuth record is rejected over `url` | The token endpoint isn't HTTPS on port 443, or its hostname doesn't resolve publicly |
| Creating or updating an OAuth record is rejected with `400` code `1300` | OAuth isn't enabled for your workspace. Contact your Customer Success Manager |
| Any webhooks call returns `403` with an empty body | The webhooks API isn't enabled for your workspace. Contact your Customer Success Manager |
| Updating `customBodyParams`, `customJwtClaims` or `customHeaders` is rejected with `400`, even though you added only one or two entries | The update merges into what's already stored, and the result would exceed 10 entries or 16 KB. Remove entries by sending them as `null` in the same request |
| Your authorization server returns `401` | `authMethod` doesn't match how the client is registered, or the secret is wrong |
| Your receiver returns `401` with a valid token | Usually the wrong audience for that receiver. Set `audience` in `customBodyParams` |
| Your receiver returns `403` with a valid token | The token lacks the required scope. Set `scope` in `customBodyParams` |
| Every delivery fails, and your authorization server shows the token request succeeding | Tokens are issued with 15 seconds or less of validity. Increase the token lifetime on your authorization server |
| A delivery is missing entirely after a token failure | `5xx`, `408`, and `429` responses, timeouts, and refused connections are retried automatically, but other failures aren't. Fix the cause, then [resend the notification](https://developers.fireblocks.com/reference/resend-webhook-notifications.md) |
| Token requests fail at the TLS handshake, and `webhookMtlsId` is set | The referenced mTLS record's certificate has expired, or your authorization server doesn't trust the CA that signed it. See [mTLS troubleshooting](https://developers.fireblocks.com/reference/webhooks-mtls.md#troubleshooting) |
| Token requests stopped presenting a client certificate | The mTLS record was deleted with `forceDelete=true`, which cleared `webhookMtlsId` on the OAuth record. Create an mTLS record and set `webhookMtlsId` again |

For the exact status and error codes on the OAuth record endpoints themselves, see [Create OAuth credentials](https://developers.fireblocks.com/api-reference/webhooks-v2/create-oauth-credentials.md) and [Update OAuth credentials](https://developers.fireblocks.com/api-reference/webhooks-v2/update-oauth-credentials.md).

## Related

* [Advanced Webhook Controls](https://developers.fireblocks.com/reference/webhooks-advanced-controls.md)
* [mTLS for webhooks](https://developers.fireblocks.com/reference/webhooks-mtls.md)
* [Custom headers for webhooks](https://developers.fireblocks.com/reference/webhooks-custom-headers.md)
* [Validating webhooks](https://developers.fireblocks.com/reference/validating-webhooks.md)
* [Configuring webhooks](https://developers.fireblocks.com/reference/webhooks-gettingstarted-configuringwebhooks.md)
