> ## 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.

# mTLS for Webhooks

> Have Fireblocks present a client certificate signed by your own certificate authority on every webhook delivery.

Your endpoint may require a client certificate on every inbound connection, authenticating the caller at the TLS handshake before any request data is exchanged. This is typically driven by your organization's security policy, and it is common at banks and other financial institutions.

With mTLS, Fireblocks presents a certificate that your own certificate authority (CA) has signed, so deliveries satisfy that requirement without any change to it. You decide what is admitted, because you control the signing. This is in addition to the JWS signature on the body, not a replacement for it.

A certificate is stored as a record in your workspace and referenced from each webhook that needs it, so one certificate covers many webhooks and rotates in a single call.

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

## How it works

Fireblocks holds the private key. You sign the Fireblocks certificate signing request (CSR) with your own CA, which makes your CA the only party that can issue a certificate Fireblocks presents to your endpoint.

1. Fetch the CSR from Fireblocks.
2. Sign it with your CA.
3. Create an mTLS record holding the signed certificate.
4. Reference that record from each webhook.

Fireblocks never receives your CA private key, and Fireblocks does not act as a certificate authority.

## Before you start

Your endpoint must request and verify client certificates during the TLS handshake, and must trust the CA that signs the CSR.

Sign the CSR with a private or managed CA that issues client authentication certificates, rather than a publicly trusted one. Following the Chrome Root Program requirements, public CAs are removing the `clientAuth` extended key usage (EKU) from TLS certificates, so a private CA is the durable option, and it also keeps the trust relationship scoped to your own infrastructure rather than to every client a public root trusts.

### Certificate requirements

Fireblocks rejects a certificate that doesn't meet these requirements when you create or update the record:

| Requirement | Accepted values |
| - | - |
| Issued over | The CSR from your workspace |
| Key | The key in the CSR Fireblocks issued: RSA 2048 or ECDSA P-256, matching the `keyAlgorithm` you requested |
| CA signature algorithm | SHA-256 or SHA-512 with RSA, SHA-256 with RSA-PSS, or ECDSA with SHA-256 or SHA-384 |
| Validity | Valid at upload and not expired |
| Chain order | The leaf certificate first |

Fireblocks recommends the `clientAuth` EKU, which marks the certificate for TLS client authentication. Fireblocks doesn't require it, but many servers reject a client certificate without it, so a certificate lacking it can be accepted when you create the record and still fail at your endpoint during the handshake.

## Enable mTLS

### 1. Fetch the CSR

Call [Get mTLS CSR](https://developers.fireblocks.com/api-reference/webhooks-v2/get-mtls-csr.md). It returns the CSR in PEM format.

Pass `keyAlgorithm` to choose between an `RSA` key, which is the default, and an `ECDSA` key. ECDSA keys are smaller, but some CAs don't accept an EC subject key by default, so check what yours supports before choosing it.

### 2. Sign the CSR

Sign the CSR with your CA, producing a leaf certificate that meets the [certificate requirements](#certificate-requirements). Keep the certificate lifetime consistent with your own PKI policy.

### 3. Create the mTLS record

Call [Create an mTLS configuration](https://developers.fireblocks.com/api-reference/webhooks-v2/create-an-mtls-configuration.md) with the signed certificate and an optional label:

```json theme={"system"}
{
  "name": "Production delivery cert",
  "signedCert": "-----BEGIN CERTIFICATE-----\nMIID...\n-----END CERTIFICATE-----"
}
```

The response returns the record with its `id` and the certificate's `expiresAt`. A certificate that wasn't issued over a CSR from your workspace is rejected. Uploading a certificate that's already stored returns the existing record rather than creating a second one.

### 4. Reference it from a webhook

Set `webhookMtlsId` 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"}
{
  "url": "https://webhooks.example.com/fireblocks",
  "webhookMtlsId": "e5f6a7b8-1234-5678-9abc-def012345678"
}
```

Reference the same `webhookMtlsId` from as many webhooks as you need. To stop presenting a certificate on one webhook without affecting the others, set `webhookMtlsId` to `null` on that webhook.

## Manage certificate records

| Operation | Endpoint |
| - | - |
| List all | [List the uploaded mTLS configurations](https://developers.fireblocks.com/api-reference/webhooks-v2/list-the-uploaded-mtls-configurations.md) |
| Read one | [Get an mTLS configuration by id](https://developers.fireblocks.com/api-reference/webhooks-v2/get-an-mtls-configuration-by-id.md) |
| Update | [Update an mTLS configuration](https://developers.fireblocks.com/api-reference/webhooks-v2/update-an-mtls-configuration.md) |
| Delete | [Delete an mTLS configuration](https://developers.fireblocks.com/api-reference/webhooks-v2/delete-an-mtls-configuration.md) |

Fetching the CSR and creating, updating, and deleting an mTLS record require the Owner, Admin, or Non-Signing Admin role.

### Rotate a certificate

Fetch the CSR, have your CA sign a new certificate, and update the record with only the new certificate:

```json theme={"system"}
{ "signedCert": "-----BEGIN CERTIFICATE-----\nMIID...\n-----END CERTIFICATE-----" }
```

Update is a partial change, so send only the fields you're changing. Every webhook and OAuth record referencing the mTLS record uses the new certificate from its next connection, so one call rotates all of them.

The CSR stays tied to the same Fireblocks key until that key's 2-year lifetime ends, so fetching it again during that time returns a CSR for the same key.

### Delete a certificate record

Deleting a record affects every webhook and OAuth record that references it, so the delete is refused with `409 Conflict` while anything still references the record, and the response names the referencing webhooks and OAuth records. Repeat the request with `forceDelete=true` to detach all of them and delete the record in one call. The response lists the detached IDs in `detachedWebhookIds` and `detachedWebhookOauthIds`. A detached webhook stops presenting a client certificate immediately, and a detached OAuth record keeps requesting tokens without one, so an endpoint that requires mTLS begins rejecting them. The webhooks and OAuth records themselves are never deleted.

To stop using mTLS on a single webhook, set that webhook's `webhookMtlsId` to `null` instead of deleting the record.

## Certificate expiry

When a certificate expires, deliveries to every webhook referencing it fail at the TLS handshake. Whether a delivery is retried depends on how the failure surfaces. Timeouts, reset connections, refused connections, and `408`, `429`, and `5xx` responses are retried, and any other failure marks the notification `FAILED`. If an OAuth record references the same mTLS record, its token requests fail at the handshake too.

These failures count toward the webhook's error rate, so a webhook that crosses the [circuit breaker](https://developers.fireblocks.com/reference/webhooks-best-practices.md#circuit-breaker) threshold is suspended. Track the `expiresAt` value returned on each record and rotate before it passes. Once you've rotated the certificate, redeliver what was missed with [Resend failed notifications](https://developers.fireblocks.com/api-reference/webhooks-v2/resend-failed-notifications.md).

The Fireblocks Console is read-only for mTLS. It shows an `mTLS` tag on each webhook, with the certificate's expiry date. Renewing the certificate is done through the API.

## mTLS on the token request

If your authorization server requires a client certificate, set `webhookMtlsId` on the [OAuth record](https://developers.fireblocks.com/reference/webhooks-oauth.md#optional-token-request-parameters) as well as on the webhook. Both can reference the same mTLS record, so one certificate serves the connection to your token endpoint and the delivery to your receiver, and rotating that record updates both. [RFC 8705](https://www.rfc-editor.org/rfc/rfc8705) certificate-bound access tokens require this, because your receiver checks that the token was issued to the certificate presenting it.

You can also reference different mTLS records from the webhook and the OAuth record, as long as both certificates are signed from your workspace's CSR.

## Troubleshooting

| Symptom | Likely cause |
| - | - |
| Certificate rejected when you create or update the record | It doesn't meet the [certificate requirements](#certificate-requirements). The usual causes are a certificate signed against a CSR for the other key algorithm, an unsupported CA signature algorithm, an expired certificate, or a chain that doesn't start with the leaf certificate |
| Handshake fails at your endpoint, although the certificate was accepted | The certificate is probably missing the `clientAuth` EKU. Reissue it from your CA with `clientAuth` set |
| Notifications marked `FAILED` and the webhook suspended | The certificate expired and the handshake failures pushed the webhook over the circuit breaker threshold. Rotate the certificate, re-enable the webhook, and [resend failed notifications](https://developers.fireblocks.com/api-reference/webhooks-v2/resend-failed-notifications.md) |
| Handshake fails, and `clientAuth` is present | Your endpoint doesn't trust the signing CA. Add your CA to the endpoint's client trust store |
| Deliveries or token requests stopped presenting a certificate | The record was deleted with `forceDelete=true`, which cleared `webhookMtlsId` on every webhook and OAuth record that referenced it. Create a record and set `webhookMtlsId` again |

## Related

* [Advanced Webhook Controls](https://developers.fireblocks.com/reference/webhooks-advanced-controls.md)
* [OAuth 2.0 for webhooks](https://developers.fireblocks.com/reference/webhooks-oauth.md)
* [Custom headers for webhooks](https://developers.fireblocks.com/reference/webhooks-custom-headers.md)
* [Validating webhooks](https://developers.fireblocks.com/reference/validating-webhooks.md)
