---
title: "mTLS"
description: "Require a client certificate on API requests through the mTLS endpoint, and present a client certificate on webhook deliveries to your receivers."
canonical: "https://bird.com/docs/guides/mtls"
availability: "on-request"
---

# mTLS

Mutual TLS (mTLS) adds a certificate check to the TLS handshake in both directions. Each direction has its own settings. You can set up one direction or both.

| Direction          | TLS client   | TLS server        | Settings                                                                        |
| ------------------ | ------------ | ----------------- | ------------------------------------------------------------------------------- |
| API requests       | Your systems | The mTLS endpoint | [**Workspace settings** > **mTLS**](https://bird.com/dashboard/w/settings/mtls) |
| Webhook deliveries | We           | Your receivers    | [**Webhooks** > **mTLS**](https://bird.com/dashboard/w/webhooks/mtls)           |

Each party keeps its own private keys. For API requests, you give us the public certificate of your certificate authority (CA). For webhook deliveries, we give you a certificate signing request (CSR), and you return the signed public certificate.

## Before you start

- Contact your Bird representative to enable API mTLS, webhook mTLS or both for your organization. Each direction is enabled separately.
- Get a role with write access to the `access_restrictions` scope in the workspace. To send webhook test events, also get write access to the `webhooks` scope.
- For API requests, get the PEM certificate of the CA that issues your client certificates. Use the issuing CA, not the root CA.
- For webhook deliveries, prepare a CA that can sign a CSR with the `clientAuth` extended key usage.

## Require client certificates on API requests

Each region has an mTLS endpoint. It serves the same operations as the standard endpoint and asks each client for a certificate.

API mTLS applies to HTTP API requests. It does not apply to [SMTP](/docs/guides/email/smtp) or [SIP](/docs/guides/voice/sip-trunks) connections. The mode does not change how those connections authenticate.

| Region | Standard endpoint               | mTLS endpoint                        |
| ------ | ------------------------------- | ------------------------------------ |
| `eu1`  | `https://eu1.platform.bird.com` | `https://mtls.eu1.platform.bird.com` |
| `us1`  | `https://us1.platform.bird.com` | `https://mtls.us1.platform.bird.com` |

The client certificate does not replace your credential. Each request still sends an API key or service account token in the `Authorization` header.

The mode sets which endpoint accepts API key and service account requests. Dashboard sessions and OAuth tokens use the standard endpoint in each mode.

| Mode         | Standard endpoint | mTLS endpoint                                    |
| ------------ | ----------------- | ------------------------------------------------ |
| `disabled`   | Accepts requests  | Rejects requests                                 |
| `evaluating` | Accepts requests  | Accepts requests with a valid client certificate |
| `enforced`   | Rejects requests  | Accepts requests with a valid client certificate |

A valid client certificate chains to a CA of the workspace, is in its validity period and has the `clientAuth` extended key usage. If you set a required subject alternative name (SAN), the certificate must also contain it. We compare DNS names and URIs. We do not use the common name.

### Add your certificate authority

1. Open [**Workspace settings** > **mTLS**](https://bird.com/dashboard/w/settings/mtls).
2. In **Certificate authorities**, select **Add certificate authority**.
3. Paste the PEM certificate of your CA, or choose a file.
4. Select **Add certificate authority**.

The certificate must have the `CA:true` basic constraint and the `keyCertSign` key usage, and it must not be expired. A workspace can have two CAs.

If your CA also issues certificates to other parties, set **Required subject alternative name** to the identity that your client certificates carry.

### Test in evaluating mode

1. In **Mode**, select **Evaluating**, then select **Save**.
2. Configure your client to present its certificate. If an intermediate CA signs the client certificate, also send the intermediate certificate.
3. Change the base URL of your client to the mTLS endpoint.
4. Send a test request:

```bash
curl https://mtls.eu1.platform.bird.com/v1/workspace \
  --cert client.pem \
  --key client.key \
  -H "Authorization: Bearer $BIRD_API_KEY"
```

A response other than `401` shows that the certificate check passed. A `401` response contains an error code. [Troubleshoot](#troubleshoot) gives the cause of each code.

### Enforce mTLS

1. Move each integration to the mTLS endpoint.
2. In **Mode**, select **Enforced**, then select **Save**.

The standard endpoint now rejects API key and service account requests for the workspace with [`E02090`](/docs/api/errors/E02090).

A change to the CAs or the required SAN can take up to 15 minutes to apply to each request.

## Present a client certificate on webhook deliveries

We generate an RSA 2048 key pair and a CSR with the subject `CN=<workspace ID>, O=Bird`. The private key stays with us. Your CA signs the CSR, and we present the signed certificate when your receiver asks for one.

We present the certificate on webhook subscriptions and direct callbacks, including retries. These deliveries do not present it:

- `realtime.*` events
- Connector endpoints
- MCP Events subscriptions

| Mode         | Delivery behavior                                                                                                   |
| ------------ | ------------------------------------------------------------------------------------------------------------------- |
| `disabled`   | We do not present a certificate.                                                                                    |
| `evaluating` | We present the certificate when the receiver asks for one. If the receiver rejects it, we deliver again without it. |
| `enforced`   | We present the certificate. If the receiver does not ask for it or rejects it, the delivery attempt fails.          |

### Sign and upload the certificate

1. Open [**Webhooks** > **mTLS**](https://bird.com/dashboard/w/webhooks/mtls).
2. In **Certificate**, select **Create signing request**.
3. In **Signing request**, select **Download**.
4. Get the CSR signed by your CA with the `clientAuth` extended key usage.
5. Select **Upload certificate**.
6. Paste the signed certificate first, then each intermediate certificate in order. You can also choose a file.
7. Select **Upload certificate**.

We check that the certificate matches the key of the signing request, has the `clientAuth` extended key usage and is in its validity period. The **Certificate** section then shows its subject, issuer, serial number, SHA-256 fingerprint and validity period.

### Configure your receivers

1. Request a client certificate during the TLS handshake.
2. Trust the CA that signed our certificate.
3. Present a server certificate from a publicly trusted CA that matches the receiver hostname.

To accept our certificate and no other certificate from your CA, match the subject `CN=<workspace ID>` or the SHA-256 fingerprint.

### Enforce webhook mTLS

1. In **Mode**, select **Evaluating**, then select **Save**.
2. Send a test event to each webhook with [`POST /v1/webhooks/{webhook_id}/test`](/docs/api/reference/test-webhook). Make sure that `client_certificate` is `presented`. `not_requested` means the receiver did not ask for the certificate, and `rejected` means it did not accept it.
3. For [SMS callback URLs](/docs/guides/sms/callbacks), which have no webhook ID, send an SMS with `options.callback_url` set to the receiver. Check the logs of the receiver, and make sure that the callback presents our certificate. In `evaluating`, a rejected certificate also shows in [`GET /v1/webhook-callbacks/attempts`](/docs/api/reference/list-webhook-callback-attempts) as a failed attempt with a `failure_reason`.
4. In **Mode**, select **Enforced**, then select **Save**.

In `enforced` mode, a rejected certificate shows as a failed attempt in the [delivery attempts](/docs/guides/webhooks#replaying-failed-deliveries) of the webhook.

## Check revocation

Set **Certificate revocation list check** to **Full chain** to check revocation with certificate revocation lists (CRLs).

- On API requests, we check the client certificate and each intermediate certificate below your CA.
- On webhook deliveries, we check the server certificate of your receiver and each intermediate certificate below its root.

We check each CRL distribution point of the certificate. The `http` and `https` URIs of one distribution point are mirrors of the same CRL, and we use the first mirror that responds. A distribution point without an `http` or `https` URI fails the check. A certificate without CRL distribution points is not checked. We verify the signature of each CRL. If we cannot get a valid CRL, we reject the request or fail the delivery attempt.

| Limit                   | Value                                                    |
| ----------------------- | -------------------------------------------------------- |
| CRL download time       | 5 seconds for each download, 10 seconds for each request |
| CRL size                | 10 MiB                                                   |
| CRLs for each workspace | 10 for API requests and 10 for webhook deliveries        |

## Rotate certificates

We email the workspace contacts 30 days and 7 days before a CA or the webhook client certificate expires.

To rotate the CA for API requests:

1. Add the new CA. The workspace now trusts both CAs.
2. Move each integration to a client certificate from the new CA.
3. Remove the old CA.

You cannot remove the last CA while the mode is `evaluating` or `enforced`. Client certificates from your CA need no action in Bird.

To rotate the webhook client certificate:

1. Select **Create new signing request**.
2. Get the new CSR signed and upload the certificate.

The current certificate stays in use until you upload the new one. Test sends use the new certificate at once, but deliveries can keep presenting the previous one for up to a few minutes. Your receivers must accept both certificates, by CA or by fingerprint, until then.

## Automate with the API

Each dashboard action has an API operation. Send these calls to an endpoint that your mode accepts: the standard endpoint in `disabled` or `evaluating` mode, and the mTLS endpoint with a client certificate in `enforced` mode. The API key needs write access to the `access_restrictions` scope. Organization master keys cannot use them.

| Operation                                                                                                                                          | Action                                        |
| -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| [`PATCH /v1/workspace/mtls-settings`](/docs/api/reference/update-workspace-mtls-settings)                                                          | Change `state`, `expected_san` or `crl_check` |
| [`POST /v1/workspace/mtls-certificate-authorities`](/docs/api/reference/create-workspace-mtls-certificate-authority)                               | Add a CA                                      |
| [`DELETE /v1/workspace/mtls-certificate-authorities/{certificate_authority_id}`](/docs/api/reference/delete-workspace-mtls-certificate-authority)  | Remove a CA                                   |
| [`PATCH /v1/workspace/webhook-client-certificate`](/docs/api/reference/update-workspace-webhook-client-certificate)                                | Change `state` or `crl_check`                 |
| [`POST /v1/workspace/webhook-client-certificate/signing-request`](/docs/api/reference/create-workspace-webhook-client-certificate-signing-request) | Create a CSR                                  |
| [`POST /v1/workspace/webhook-client-certificate/certificate`](/docs/api/reference/submit-workspace-webhook-client-certificate)                     | Upload the signed certificate                 |

```bash
curl -X PATCH https://eu1.platform.bird.com/v1/workspace/mtls-settings \
  -H "Authorization: Bearer $BIRD_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"state": "enforced", "crl_check": "full_chain"}'
```

## Troubleshoot

API requests that fail a check return `401` with one of these codes:

| Code                                | Cause                                                                 |
| ----------------------------------- | --------------------------------------------------------------------- |
| [`E02086`](/docs/api/errors/E02086) | The request presented no client certificate.                          |
| [`E02087`](/docs/api/errors/E02087) | The client certificate is not valid for the workspace.                |
| [`E02088`](/docs/api/errors/E02088) | A certificate in the chain is revoked.                                |
| [`E02089`](/docs/api/errors/E02089) | The mode is `disabled`, so the mTLS endpoint rejects the request.     |
| [`E02090`](/docs/api/errors/E02090) | The mode is `enforced`, so the standard endpoint rejects the request. |
| [`E02091`](/docs/api/errors/E02091) | We could not download a CRL within 5 seconds.                         |
| [`E02092`](/docs/api/errors/E02092) | A CRL is larger than 10 MiB.                                          |
| [`E02093`](/docs/api/errors/E02093) | The workspace already uses 10 CRLs.                                   |
| [`E02094`](/docs/api/errors/E02094) | A CRL has an invalid signature or is past its next update time.       |

A setting change can return one of these codes:

- [`E10027`](/docs/api/errors/E10027): API mTLS is not enabled for the workspace. Contact your Bird representative to enable API mTLS for your organization. If it is already enabled, ask an organization administrator to raise the workspace's API mTLS limit.
- [`E10028`](/docs/api/errors/E10028): webhook mTLS is not enabled for the workspace. Contact your Bird representative to enable webhook mTLS for your organization. If it is already enabled, ask an organization administrator to raise the workspace's webhook mTLS limit.

## Next steps

- [Authentication](/docs/guides/authentication): API keys, scopes and service accounts
- [Webhooks & events](/docs/guides/webhooks): create endpoints and verify signatures
- [Errors](/docs/guides/errors): the error envelope and how to handle it

## Related resources

- [Webhooks done right: reliable delivery events](/learn/basics/webhooks-done-right-reliable-delivery-events) (video)
- [How do I verify a webhook signature?](/explained/platform/how-do-i-verify-a-webhook-signature) (answer)
- [SMTP relay](/email-api/features/smtp) (product)
- [Operate messaging reliably](/learn/paths/reliability) (course)
