Skip to main content
This page covers inbound webhooks โ€” evidence pushed to ReconLayer by providers and chain indexers. For the opposite direction (ReconLayer notifying your systems of state changes), see Outbound Webhooks.
Provider webhooks are one of the three ways evidence enters ReconLayer, alongside file imports and integration polling.

Provider webhooks: Bridge

Today, ReconLayer ships a native inbound webhook integration for Bridge. Each Bridge integration you configure gets its own dedicated webhook URL:
POST /webhooks/bridge/{integrationKey}
{integrationKey} is the integrationKey you chose when creating the Bridge integration (POST /v1/bridge-integrations), and itโ€™s how ReconLayer resolves which organization and which signing key the incoming payload belongs to โ€” there is no Authorization or x-organization-id header on this route.
POST /webhooks/bridge (without an {integrationKey}) is intentionally rejected with 400 invalid_request and the message โ€œUse /webhooks/bridge/:integrationKey so the webhook can resolve organization context.โ€ Configure Bridge to send webhooks to the per-integration URL only.

Signature verification

Bridge signs every webhook with the header:
X-Webhook-Signature: t=<unix_timestamp_ms>,v0=<base64_rsa_sha256_signature>
ReconLayer verifies this signature against the integrationโ€™s stored webhookPublicKey:
  1. Parses t= (timestamp, milliseconds) and v0= (base64-encoded signature) from the header. Missing or malformed headers fail verification immediately.
  2. Rejects the webhook if |now - t| exceeds a 10-minute (600,000 ms) replay tolerance window.
  3. Verifies an RSA-SHA256 signature over the string ${timestamp}.${rawBody} using the integrationโ€™s public key.
Even when the signature is invalid, ReconLayer still persists the payload as a RawRecord (with validationStatus: 'failed', signatureValid: false) for audit purposes โ€” the matching engine is skipped, but nothing is silently dropped. The 400 response includes details.rawRecordId and details.sourceRef so you can locate the record.

Responses

200 EvidenceAcceptanceResponse
object
Returned when the signature is valid. Same shape used across all evidence endpoints:
{
  "accepted": true,
  "duplicate": false,
  "rawRecordId": "rr_01h...",
  "flowLegIds": ["fl_01h..."],
  "matchedCaseIds": ["case_01h..."],
  "signatureValid": true,
  "source": "webhook",
  "sourceRef": "evt_bridge_01h..."
}
400 invalid_webhook_configuration
object
The integrationKey in the path does not resolve to a known, active Bridge integration.
400 invalid_webhook_signature
object
Signature missing, malformed, expired (outside the 10-minute window), or failed RSA-SHA256 verification.
{
  "error": "invalid_webhook_signature",
  "message": "Webhook signature verification failed.",
  "details": {
    "rawRecordId": "rr_01h...",
    "sourceRef": "evt_bridge_01h..."
  }
}
See Receive a Bridge Webhook and Reject Ambiguous Bridge Webhook Entrypoint. For how the payload is normalized into FlowLegs, see Bridge Integration.

Listing configured inbound endpoints

GET /v1/webhook-endpoints lists the inbound webhook URLs ReconLayer has generated for your active integrations (currently Bridge):
curl http://localhost:3001/v1/webhook-endpoints \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID"
{
  "items": [
    {
      "integrationId": "intg_01h...",
      "integrationKey": "acme_bridge_main",
      "provider": "bridge",
      "webhookUrl": "https://api.reconlayer.com/webhooks/bridge/acme_bridge_main",
      "status": "active"
    }
  ]
}
See List Inbound Integration Webhook Endpoints.

No native webhook for your provider? Submit evidence directly

If youโ€™re integrating a provider, bank, or system that doesnโ€™t have a native ReconLayer webhook adapter, push evidence directly through the canonical evidence endpoints. These are the same entry points used internally by Bridge polling and on-chain ingestion โ€” they go through the identical ingestEvidence pipeline and produce the same EvidenceAcceptanceResponse. Both endpoints require Authorization, x-organization-id, and an Idempotency-Key header.

POST /v1/evidence/provider

Use this for any provider/PSP-side settlement, payout, or transfer event.
curl -X POST http://localhost:3001/v1/evidence/provider \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "acme-psp",
    "sourceRef": "acme-psp:txn_8123",
    "providerTransferId": "txn_8123",
    "externalReference": "INV-2026-0099",
    "status": "confirmed",
    "amount": { "amount": "1000.00", "currency": "USD" },
    "occurredAt": "2026-06-14T09:30:00Z",
    "payload": { "raw": "original provider payload, stored as-is" }
  }'
provider
string
required
Provider identifier, e.g. "acme-psp".
sourceRef
string
required
A stable identifier unique per (organizationId, source) โ€” used for duplicate detection.
paymentIntentId
string
Link directly to a known PaymentIntent if you already know it.
providerTransferId
string
The providerโ€™s own transfer/transaction ID.
externalReference
string
Your reference, used for matching against PaymentIntent.externalReference.
phase
string
e.g. intermediary_in, transfer, intermediary_out, destination (defaults to intermediary_in for provider evidence).
status
string
default:"confirmed"
A FlowLeg status, e.g. pending, confirmed, failed, reversed.
amount
object
required
{ "amount": string, "currency": string }.
occurredAt
string
ISO 8601 timestamp of when the event occurred.
references
array
Additional FlowLegReference key/value entries.
payload
object
required
The raw payload as received from the provider โ€” stored verbatim on the RawRecord.
See Submit Provider Evidence.

POST /v1/evidence/onchain

Use this for an on-chain transfer observed by your own indexer or RPC node, for chains/providers not covered by a native on-chain integration.
curl -X POST http://localhost:3001/v1/evidence/onchain \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "chainFamily": "evm",
    "txHash": "0xabc123...",
    "chainId": 137,
    "fromAddress": "0x1111...",
    "toAddress": "0x2222...",
    "tokenAddress": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
    "externalReference": "INV-2026-0099",
    "status": "confirmed",
    "amount": { "amount": "1000.00", "currency": "USDC" },
    "occurredAt": "2026-06-14T09:30:00Z",
    "payload": { "raw": "original indexer payload" }
  }'
chainFamily
string
default:"evm"
Currently only evm is supported โ€” other values raise an error during normalization.
txHash
string
required
The on-chain transaction hash. Lowercased during normalization.
chainId
number
required
EVM chain ID (e.g. 1 for Ethereum, 137 for Polygon).
fromAddress
string
Sender address.
toAddress
string
Recipient address.
tokenAddress
string
ERC-20 token contract address, if applicable.
externalReference
string
Your reference, used for matching against PaymentIntent.externalReference.
status
string
default:"confirmed"
A FlowLeg status.
amount
object
required
{ "amount": string, "currency": string }.
sourceRef
string
Optional override for the dedup key; if omitted, derived from txHash.
payload
object
required
The raw payload โ€” stored verbatim on the RawRecord.
See Submit On-chain Evidence and On-chain Integrations for the EVM normalization details (hex address lowercasing, tx_hash/external_reference references, chainFamily/networkId metadata).

Idempotency on evidence endpoints

Both endpoints require Idempotency-Key. ReconLayer hashes the request body and stores it against the key:
  • Same key + same body โ†’ replays the original response with Idempotency-Replayed: true.
  • Same key + different body โ†’ 409 idempotency_key_conflict.
  • Same key while the original request is still processing โ†’ 409 idempotency_request_in_progress.
Both endpoints return 201 for newly accepted evidence and 200 if ingestEvidence determined the record was a (organizationId, source, sourceRef) duplicate (duplicate: true in the response body) โ€” this is independent of, and in addition to, the Idempotency-Key mechanism.

Next steps