Skip to main content
Bridge is a stablecoin payments infrastructure provider. A BridgeIntegration connects your Bridge account to ReconLayer so that transfer events — funding, on-chain movement, and payouts — are normalized into FlowLegs and matched against your PaymentIntents automatically.
Bridge evidence reaches ReconLayer through two of the three ingestion paths: inbound webhooks (real-time) and integration polling (catch-up / reconciliation).

Creating a Bridge integration

curl -X POST http://localhost:3001/v1/bridge-integrations \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Bridge — Production",
    "integrationKey": "acme_bridge_main",
    "apiKey": "sk_bridge_live_...",
    "webhookBaseUrl": "https://api.reconlayer.com",
    "eventCategories": ["transfer"],
    "pollingEnabled": true
  }'
name
string
required
Display name shown in the dashboard.
integrationKey
string
required
Lowercase, snake_case identifier (^[a-z0-9_]+$), unique to this integration. Used to build the inbound webhook URL and resolve organization context on incoming webhooks.
apiKey
string
required
Your Bridge API key, sent as Api-Key on calls to the Bridge API (https://api.bridge.xyz/v0). Never returned by the API — responses only include hasApiKey: true.
webhookBaseUrl
string
required
Must be https://. ReconLayer derives the full webhook URL by appending /webhooks/bridge/{integrationKey} to this base (path normalized to avoid double slashes).
eventCategories
string[]
default:"[\"transfer\"]"
At least one of: customer, kyc_link, liquidation_address.drain, static_memo.activity, transfer, virtual_account.activity, bridge_wallet.activity, card_account, card_transaction, card_withdrawal, posted_card_account_transaction, external_account.
pollingEnabled
boolean
default:"true"
Whether the background poll loop should include this integration.
On creation, ReconLayer:
  1. Builds the webhook URL: ${webhookBaseUrl}/webhooks/bridge/${integrationKey}.
  2. Calls the Bridge API to register a webhook for the requested eventCategories (POST /webhooks on https://api.bridge.xyz/v0, with an Idempotency-Key and event_epoch: 'webhook_creation').
  3. Stores the resulting webhookId, webhookUrl, and webhookStatus on the BridgeIntegrationDetail.
Response (BridgeIntegrationDetail):
{
  "id": "bint_01h...",
  "organizationId": "org_01h...",
  "name": "Acme Bridge — Production",
  "integrationKey": "acme_bridge_main",
  "webhookId": "wh_01h...",
  "webhookUrl": "https://api.reconlayer.com/webhooks/bridge/acme_bridge_main",
  "webhookStatus": "active",
  "eventCategories": ["transfer"],
  "hasApiKey": true,
  "pollingEnabled": true,
  "lastTransferSyncedAt": null,
  "createdAt": "2026-06-14T09:00:00.000Z",
  "updatedAt": "2026-06-14T09:00:00.000Z"
}
webhookStatus mirrors Bridge’s own status enum: disabled, active, deleted. See Create a Bridge Integration.
If webhookBaseUrl is not https://, the request fails with 400 invalid_request (BridgeIntegrationValidationError: “Bridge webhook base URL must use https.”). Bridge integration calls that fail upstream surface as 502 bridge_upstream_error with an upstreamStatus reflecting Bridge’s HTTP status.

Managing integrations

# List
curl http://localhost:3001/v1/bridge-integrations \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID"

# Get detail
curl http://localhost:3001/v1/bridge-integrations/bint_01h... \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID"

# Update event categories or polling
curl -X PATCH http://localhost:3001/v1/bridge-integrations/bint_01h... \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{ "eventCategories": ["transfer", "external_account"], "pollingEnabled": true }'
If you change webhookBaseUrl while the integration’s webhookStatus is active, the request fails with 400 (“Disable the Bridge integration before changing its webhook URL.”) — disable first, update, then re-enable.

Enable / disable

curl -X POST http://localhost:3001/v1/bridge-integrations/bint_01h.../enable \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID"

curl -X POST http://localhost:3001/v1/bridge-integrations/bint_01h.../disable \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID"
See List Bridge Integrations, Get Bridge Integration Detail, Update a Bridge Integration, Enable a Bridge Integration, and Disable a Bridge Integration.

Receiving webhooks

Bridge sends events to POST /webhooks/bridge/{integrationKey}. ReconLayer verifies the X-Webhook-Signature (RSA-SHA256, 10-minute replay tolerance) before running matching — see Inbound Webhooks for the full signature verification flow and error responses.
curl -X GET http://localhost:3001/v1/webhook-endpoints \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID"

Webhook normalization

Every Bridge webhook payload becomes a RawRecord with source: 'webhook', sourceType: 'psp_report', provider: 'bridge', and integrationKey set to your integration’s key. normalizedType is taken from event_type (falling back to event_category, then 'bridge.webhook'), and normalizedId is the Bridge transfer ID (falling back to sourceRef). From a single webhook, ReconLayer can produce up to two FlowLegs:
Created whenever the payload carries a Bridge transfer ID. Its status is derived from event_object_status / the transfer’s state via this mapping:
Bridge stateFlowLeg.status
funds_received, payment_processedconfirmed
returned, refunded, canceledreversed
error, undeliverable, refund_failedfailed
anything elsepending
FlowLegReferences captured: bridge_event_id, bridge_event_type, external_reference (from Bridge’s client_reference_id), customer_id (from on_behalf_of), destination_account_id.
Created when the payload includes a txHash, or when the source/destination payment rail is polygon or ethereum. Uses the same Bridge-state-to-status mapping above.chainId is derived from the payment rail:
Payment railchainId
ethereum1
polygon137
FlowLegReferences captured: tx_hash, bridge_transfer_id, external_reference. Includes fromAddress, toAddress, and tokenAddress when present in the Bridge payload.

Polling

In addition to (or instead of) webhooks, ReconLayer can poll Bridge for transfer state directly:
curl -X POST http://localhost:3001/v1/bridge-integrations/bint_01h.../poll-transfer \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{ "transferId": "transfer_01h..." }'
transferId
string
required
The Bridge transfer ID to fetch via GET /transfers/:id on the Bridge API.
Response (BridgeTransferPollResponse):
{
  "transferId": "transfer_01h...",
  "transferState": "payment_processed",
  "evidence": {
    "accepted": true,
    "duplicate": false,
    "rawRecordId": "rr_01h...",
    "flowLegIds": ["fl_01h..."],
    "matchedCaseIds": ["case_01h..."],
    "signatureValid": true,
    "source": "api",
    "sourceRef": "transfer_01h...:2026-06-14T09:30:00Z"
  }
}
A polled transfer is wrapped as a synthetic webhook payload (event_category: 'transfer', event_type: 'bridge.transfer.polled') and run through the same normalization described above, with source: 'api', signatureValid: true always, and sourceRef set to {transferId}:{updatedAt|createdAt|state}. normalizedType becomes bridge.transfer.{state} (or bridge.transfer.snapshot if no state is available). Re-polling the same transfer is deduplicated by comparing the transfer’s updated_at/created_at/state (whichever is set first) against the value already recorded — if nothing changed, the existing record is returned as a duplicate rather than reprocessed. See Poll a Bridge Transfer.

Background poll loop

When pollingEnabled: true, the Bridge poll loop (apps/api/src/bridge-poller.ts) periodically calls pollActiveBridgeIntegrations across all active, polling-enabled integrations and feeds any new/changed transfers through ingestEvidence, using actor system:bridge_poller.
Environment variableDefaultPurpose
BRIDGE_POLL_INTERVAL_MS60000Interval between poll cycles. Set to 0 or less to disable the poll loop entirely.
BRIDGE_POLL_TRANSFER_LIMIT25Maximum transfers fetched per integration per cycle.

Next steps

  • Inbound Webhooks — signature verification details and the canonical evidence endpoints for providers without a native adapter.
  • Ingestion Flows — how Bridge evidence flows into the matching engine.
  • On-chain Integrations — for chains/networks beyond what a Bridge onchain_transfer leg captures.