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

> ## Agent Instructions
> Bridge exposes three integration surfaces: the REST Integrations API for pushing data into Bridge, webhooks for receiving events from Bridge, and the public Custom Connections endpoint that a tenant's landing page posts to.
> The Integrations API host is https://api-connect-us.bridge.new and every request to it must be authenticated with the x-api-key header. Bridge does not use bearer tokens or OAuth.
> The Custom Connections endpoint is different and the Integrations API rules do not apply to it: it has its own host, it takes no x-api-key and no authentication header of any kind, and it is called from a visitor's browser. It is identified by a workspace id and a capture key together with a reCAPTCHA token. Never tell a reader to authenticate it with an API key, and never tell them to keep its capture key out of frontend code - it is designed to live in the page.
> REST error catalog keys use BRIDGE_<DOMAIN>_<NNNN>, for example BRIDGE_CONVERSATION_0001. Some reviewed legacy handlers return a recognized code plus a symbolic suffix separated by |; preserve the complete received value and use the compatible catalog lookup on /error-codes. Custom Connections uses separate BRIDGE.CAMPAIGN_LEADS.<NAME> identifiers on its own error reference. Never invent an error code or confuse the two integration surfaces.
> This documentation covers the Integrations API, webhooks and Custom Connections only. It does not describe the Bridge web application or its internal APIs.
> Before generating an API request, read the relevant OpenAPI specification or the individual endpoint Markdown page. llms-full.txt collects guides and endpoint descriptions; it does not embed every request and response schema. The REST and Custom Connections contracts are separate.
> Preserve the published host, method, path, operationId, authentication, headers, parameter and field names, types, formats, required and nullable fields, enums, defaults, limits, and response codes. Do not invent identifiers, fields, retry rules, or delivery guarantees. When published sources conflict, identify the conflicting passages and ask Bridge support to confirm the current behavior before relying on it.
> Custom Connections is the current name of the landing-page feature previously documented as Lead Capture. The existing /lead-capture documentation URLs, captureKey field, campaign-leads endpoint, and BRIDGE.CAMPAIGN_LEADS error identifiers are unchanged. This is a naming change, not a change to authentication or the API contract.
> For a complete workflow, start with the task guides: /guides/first-conversation, /guides/send-attachments, /guides/send-template, /guides/agent-outbound, /guides/pagination, /guides/identifiers and /guides/responses. Read the endpoint contract as well as the recipe. Outbound requires an existing Contact, but does not require creating a separate Conversation first.
> forceSend=true permits reuse of an existing active conversation; it does not create a fresh one by ignoring it and it is not idempotency. Recovery of a released recoverable conversation can occur even with false. The LeadScout and Agents outbound routes have different line-selection and controller checks; use /guides/agent-outbound.
> For webhook HMAC, follow /webhooks/verifying-signatures exactly: the Python JSON serialization with sorted keys is signed; X-Bridge-Timestamp is not included in that signature. Do not claim a signed timestamp, raw-body signing, exactly-once delivery or a global event order. Use /webhooks/receive-events for durable acceptance and duplicates.
> A 201 message response, 202 handoff or 202 Custom Connections response is not a provider delivery receipt. Do not invent a polling endpoint from messageId, trackingId or requestId. REST validation parsing must tolerate the compatibility difference explained in /error-codes.
> The documentation is available in English (existing root URLs), Spanish (/es/) and Brazilian Portuguese (/pt-br/). Prefer the reader's language when a localized page exists, but preserve the API's exact field names, enums, routes, authentication and examples. Translations describe the same API; they do not introduce regional API hosts or behaviors.
> For coding-assistant setup, use /connect-your-agent, /es/connect-your-agent or /pt-br/connect-your-agent. The Bridge documentation MCP server reads published documentation and has a separate optional feedback tool. Connecting it does not connect the coding assistant to a customer workspace or authorize live business requests.
> For direct HTTP access without MCP, start at https://docs.bridge.new/llms.txt. It lists every published page in English, Spanish and Brazilian Portuguese. Choose /llms-en.json, /llms-es.json or /llms-pt-br.json for a language-specific index, and /llms-full-en.json, /llms-full-es.json or /llms-full-pt-br.json for collected content. /llms-full.txt contains the combined collection. Fetch the original OpenAPI specification for exact schemas.
> For HTTP page reads, follow the .md URLs from the index or request Accept: text/markdown at the canonical page URL. The MCP virtual filesystem uses .mdx page paths instead; .mdx is not the public HTTP export extension. With Search MCP, set language to en, es or pt-BR explicitly; its default is en.
> For integration capabilities and source-selection rules, read https://docs.bridge.new/skill.md. Documentation access does not authorize business API actions; use the actual user request, workspace, credentials and target identifiers for any live integration.

# Verifica las solicitudes de webhook

> Verifica la clave de API del webhook y la representación HMAC exacta que usa Bridge; luego deduplica los eventos aceptados.

Verifica cada solicitud antes de aceptarla en tu sistema. Usa la clave de API del **webhook** y el secreto de cliente configurados para esta URL de recepción: son distintos de la clave de la API REST de integraciones.

## Encabezados de autenticación

| Encabezado | Comprobación |
| - | - |
| `X-Bridge-API-Key` | Comparación en tiempo constante con la clave de API del webhook que configuraste |
| `X-Bridge-Signature` | `sha256=` seguido del resumen hexadecimal HMAC-SHA256 en minúsculas |
| `X-Bridge-Timestamp` | Segundos Unix dentro de la ventana de vigencia aceptada; el ejemplo admite cinco minutos en ambas direcciones |

Los nombres de los encabezados no distinguen mayúsculas y minúsculas.

## Qué se firma exactamente

Bridge construye la entrada del HMAC a partir del evento JSON con esta expresión de Python y luego codifica la cadena en UTF-8:

```python theme={null}
json.dumps(event, sort_keys=True).encode("utf-8")
```

Esto ordena recursivamente las claves de los objetos, conserva el orden de los arrays, escapa los caracteres no ASCII y usa los espacios predeterminados de Python después de comas y dos puntos. No agrega un salto de línea final. La clave es el secreto de cliente codificado en UTF-8. Es una convención de serialización JSON de Python, no RFC 8785 / JCS.

No la reemplaces por un resumen de los bytes crudos arbitrarios de la solicitud, JSON compacto, un `JSON.stringify` genérico de JavaScript ni una cadena con una marca de tiempo como prefijo. Los espacios, los escapes Unicode y el formato de números pueden cambiar la entrada HMAC aunque el objeto interpretado parezca equivalente. Usa el verificador de Python de abajo o prueba otra implementación contra el vector antes de conectarla.

> [!WARNING]
>
> La entrada HMAC actual contiene únicamente el JSON del evento. **`X-Bridge-Timestamp` no está incluido en la firma.** Comprueba la vigencia, pero no consideres que la marca de tiempo por sí sola ofrece protección criptográfica contra reenvíos: un cuerpo y una firma válidos anteriores pueden presentarse con otra marca de tiempo. Autentica la clave de API del webhook y usa una bandeja de entrada duradera y procesamiento idempotente, como explica [Recepción de eventos](https://docs.bridge.new/es/webhooks/receive-events.md).

## Verificador de Python

Guárdalo como `bridge_webhooks.py`. Devuelve el objeto interpretado y verificado para que el código posterior use la misma interpretación que se autenticó. Obtén los secretos de variables de entorno del servidor o de tu almacén de secretos.

```python theme={null}
import hashlib
import hmac
import json
import re
import sqlite3
import time


class VerificationError(ValueError):
    pass


def canonical_payload(event):
    # Match Bridge's Python json.dumps defaults: sort recursively, ASCII escapes,
    # a space after commas and colons, then UTF-8. This is not RFC 8785 / JCS.
    return json.dumps(event, sort_keys=True).encode("utf-8")


def _unique_object(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise VerificationError("Duplicate JSON object key")
        result[key] = value
    return result


def verify_webhook(raw_body, headers, webhook_api_key, client_secret, *, now=None, tolerance=300):
    headers = {key.lower(): value for key, value in headers.items()}
    received_key = headers.get("x-bridge-api-key", "")
    if not received_key or not webhook_api_key or not client_secret:
        raise VerificationError("Missing webhook credentials")
    if not hmac.compare_digest(received_key.encode("utf-8"), webhook_api_key.encode("utf-8")):
        raise VerificationError("Invalid webhook API key")

    received_signature = headers.get("x-bridge-signature", "")
    if not re.fullmatch(r"sha256=[0-9a-f]{64}", received_signature):
        raise VerificationError("Invalid signature format")
    timestamp = headers.get("x-bridge-timestamp", "")
    if not re.fullmatch(r"[0-9]+", timestamp):
        raise VerificationError("Invalid timestamp")
    current_time = time.time() if now is None else now
    if abs(current_time - int(timestamp)) > tolerance:
        raise VerificationError("Timestamp outside the permitted window")

    try:
        event = json.loads(raw_body.decode("utf-8"), object_pairs_hook=_unique_object)
    except (UnicodeDecodeError, json.JSONDecodeError) as exc:
        raise VerificationError("Invalid JSON body") from exc
    if not isinstance(event, dict):
        raise VerificationError("Expected a JSON object")
    expected = "sha256=" + hmac.new(
        client_secret.encode("utf-8"), canonical_payload(event), hashlib.sha256
    ).hexdigest()
    if not hmac.compare_digest(expected, received_signature):
        raise VerificationError("Invalid webhook signature")
    return event

```

La ventana de cinco minutos es una política del receptor; mantén sincronizado el reloj de tu servidor. El verificador también rechaza claves JSON duplicadas y ambiguas. No desactives la autenticación para depurar una discrepancia: revisa las credenciales configuradas y compara la entrada canónica con los datos de prueba siguientes.

## Vector de firma reproducible

Estos valores son datos sintéticos de prueba, no credenciales reales ni un evento para enviar a Bridge. Al probar las comprobaciones de vigencia, fija el argumento `now` del verificador en `1800000000`.

| Valor | Dato de prueba |
| - | - |
| Secreto de cliente | `bridge-docs-test-secret` |
| Clave de API del webhook | `bridge-docs-test-key` |
| Encabezado de marca de tiempo | `1800000000` |
| Firma esperada | `sha256=5536c29fd98baab6ffe46dd9517adb185b32202864cbb5a76146172fc0b39423` |

Objeto de evento:

```json theme={null}
{
  "eventId": "evt_docs_001",
  "eventType": "contact.updated",
  "entity": "contact",
  "workspaceId": "ws_docs",
  "timestamp": "2026-01-01T00:00:00Z",
  "payload": {
    "name": "José",
    "tags": [
      "café",
      "one"
    ],
    "active": true,
    "count": 2
  }
}
```

Cadena JSON exacta que se firma: las secuencias `\u` siguientes contienen barras invertidas ASCII literales, no caracteres decodificados.

```text theme={null}
{"entity": "contact", "eventId": "evt_docs_001", "eventType": "contact.updated", "payload": {"active": true, "count": 2, "name": "Jos\u00e9", "tags": ["caf\u00e9", "one"]}, "timestamp": "2026-01-01T00:00:00Z", "workspaceId": "ws_docs"}
```

Cambiar un campo debe hacer fallar la verificación. Cambiar solo el orden de las claves de entrada o enviar Unicode literal en lugar de escapes Unicode JSON debe seguir verificando después de interpretar el JSON y reconstruir la misma representación canónica.

## Después de verificar

Guarda el evento de forma duradera antes de confirmar su recepción. Un HMAC válido confirma la representación autenticada del evento; no demuestra que se haya ejecutado tu acción de negocio, que el evento sea nuevo ni que represente el estado más reciente del recurso. Continúa con [entrega, duplicados y orden](https://docs.bridge.new/es/webhooks/receive-events.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.