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

# Securing Data Connections

> Learn how to verify that data connection WebSocket requests are authentically from Ultravox.

When you use a [data connection](/tools/custom/http-vs-client-tools#data-connection-tools), Ultravox connects to your WebSocket endpoint. To ensure your server only accepts connections from Ultravox, you can use custom headers for simple token-based authentication or shared secrets for HMAC-SHA256 signature verification.

## Custom Headers

The simplest way to authenticate data connections is to pass headers that your server can check. Use the `headers` field in the data connection config to send literal key-value pairs on the outbound WebSocket connection.

```js Data connection with custom headers theme={null}
{
  "dataConnection": {
    "websocketUrl": "wss://your-server.com/data",
    "headers": {
      "Authorization": "Bearer your-token",
      "X-Custom-Header": "custom-value"
    }
  }
}
```

Your server can then verify the header values before accepting the connection.

## Shared Secrets

For stronger verification, shared secrets let your server cryptographically verify that a connection is from Ultravox and hasn't been tampered with. Ultravox uses HMAC-SHA256 signatures — when shared secrets are configured, every outbound data connection WebSocket request includes three headers:

| Header                           | Description                                                                   |
| -------------------------------- | ----------------------------------------------------------------------------- |
| `X-Ultravox-Call-ID`             | The UUID of the call initiating the connection.                               |
| `X-Ultravox-Signature-Timestamp` | ISO 8601 UTC timestamp of when the connection was made.                       |
| `X-Ultravox-Signature`           | HMAC-SHA256 signature(s), comma-separated if multiple secrets are configured. |

The signature is computed as:

```
HMAC-SHA256(secret, call_id + timestamp)
```

### Setting Shared Secrets

Shared secrets can be set at the call level or on an agent's call template. Secrets must be between 16 and 127 characters. They are write-only and never returned in API responses.

```js Setting shared secrets during call creation theme={null}
const response = await fetch('https://api.ultravox.ai/api/calls', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your_api_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    systemPrompt: "You are a helpful assistant...",
    model: "ultravox-v0.7",
    dataConnection: {
      websocketUrl: "wss://your-server.com/data"
    },
    sharedSecrets: ["your-secret-at-least-16-chars"]
  })
});
```

```js Setting shared secrets on an agent template theme={null}
const response = await fetch('https://api.ultravox.ai/api/agents', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your_api_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: "My Agent",
    callTemplate: {
      systemPrompt: "You are a helpful assistant...",
      sharedSecrets: ["your-secret-at-least-16-chars"]
    }
  })
});
```

Secrets set at call creation override those from the agent template.

### Verifying Signatures

When your data connection server receives an incoming WebSocket connection from Ultravox, verify it with these steps:

<Steps>
  <Step title="Timestamp Verification">
    * The `X-Ultravox-Signature-Timestamp` header contains the time the connection was initiated.
    * Verify that this timestamp is recent (e.g. within the last minute) to prevent replay attacks.
  </Step>

  <Step title="Signature Verification">
    * Ultravox signs each connection using HMAC-SHA256.
    * The signature is in the `X-Ultravox-Signature` header.
    * To verify:
      * Concatenate the call ID (`X-Ultravox-Call-ID`) with the timestamp.
      * Create an HMAC-SHA256 hash using your shared secret as the key.
      * Compare this hash with the provided signature.

    ```python Verifying Data Connection Signature theme={null}
    import datetime
    import hmac

    call_id = request.headers["X-Ultravox-Call-ID"]
    timestamp = request.headers["X-Ultravox-Signature-Timestamp"]
    if datetime.datetime.now(datetime.timezone.utc) - datetime.datetime.fromisoformat(timestamp) > datetime.timedelta(minutes=1):
      raise RuntimeError("Expired message")
    expected_signature = hmac.new(SHARED_SECRET.encode(), (call_id + timestamp).encode(), "sha256").hexdigest()
    for signature in request.headers["X-Ultravox-Signature"].split(","):
      if hmac.compare_digest(signature.strip(), expected_signature):
        break  # Valid signature
    else:
      raise RuntimeError("Invalid signature")
    ```
  </Step>

  <Step title="Multiple Signatures">
    * The `X-Ultravox-Signature` header may contain multiple comma-separated signatures.
    * This supports key rotation without downtime — configure both old and new secrets, then remove the old one once your server is updated.
    * Your server should check if **any** of the provided signatures match.
  </Step>
</Steps>
