Skip to main content
On-chain integrations connect ReconLayer to a blockchain indexer or RPC provider so that confirmed transfers β€” stablecoin payouts, on-chain settlements, treasury movements β€” become FlowLegs of type onchain_transfer and are matched against your PaymentIntents.
EVM is currently the only supported networkFamily. normalizeOnchainEvidenceByFamily and normalizeOnchainIntegrationTransferIngestByFamily switch on chainFamily/networkFamily and throw for any value other than 'evm'.

Two ways to bring on-chain evidence in

PathWhen to use it
On-chain integration (POST /v1/onchain-integrations + .../ingest)You run (or operate) an indexer that watches specific addresses on a specific network, and want ReconLayer to track sync state (lastTransferSyncedAt) per integration.
Direct evidence submission (POST /v1/evidence/onchain)A one-off or ad-hoc submission β€” e.g. from your own backend after observing a transaction β€” without registering a persistent integration. See Inbound Webhooks.
Both paths converge on the same EVM normalization and the same ingestEvidence pipeline described in Ingestion Flows.

Creating an on-chain integration

curl -X POST http://localhost:3001/v1/onchain-integrations \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Polygon USDC Treasury",
    "provider": "alchemy",
    "networkFamily": "evm",
    "networkId": "137",
    "apiKey": "alch_...",
    "integrationKey": "polygon_usdc_treasury",
    "pollingEnabled": true,
    "pushEnabled": false,
    "watchedAddresses": ["0x1111111111111111111111111111111111111111"],
    "startBlock": 55000000
  }'
name
string
required
Display name.
provider
string
required
Your indexer/RPC provider name, e.g. "alchemy", "infura", "custom".
networkFamily
string
default:"evm"
Currently must be "evm".
networkId
string
required
Network/chain identifier as a string, e.g. "1" (Ethereum) or "137" (Polygon). Stored as networkId and stamped into ingested leg metadata.
apiKey
string
required
Credential for your indexer/provider. Never returned β€” responses only include hasApiKey: true.
integrationKey
string
Pattern ^[a-z0-9:_-]+$ (case-insensitive). Auto-generated if omitted.
pollingEnabled
boolean
default:"true"
Whether ReconLayer (or your poller) can call .../ingest with source: "poll".
pushEnabled
boolean
default:"false"
Whether .../ingest accepts source: "push" calls (e.g. from a webhook-driven indexer).
watchedAddresses
string[]
default:"[]"
required
Addresses this integration is responsible for monitoring.
startBlock
number
Optional starting block height for backfill/sync purposes (0 to 2^53-1).
At least one of pollingEnabled / pushEnabled must be true β€” creating or updating an integration with both false returns 400 invalid_request (β€œEnable polling or push ingestion before saving the on-chain integration.”).
Response (OnchainIntegrationDetail):
{
  "id": "ocint_01h...",
  "organizationId": "org_01h...",
  "provider": "alchemy",
  "networkFamily": "evm",
  "networkId": "137",
  "name": "Polygon USDC Treasury",
  "integrationKey": "polygon_usdc_treasury",
  "status": "disabled",
  "hasApiKey": true,
  "pollingEnabled": true,
  "pushEnabled": false,
  "watchedAddressCount": 1,
  "watchedAddresses": ["0x1111111111111111111111111111111111111111"],
  "startBlock": 55000000,
  "lastTransferSyncedAt": null,
  "createdAt": "2026-06-14T09:00:00.000Z",
  "updatedAt": "2026-06-14T09:00:00.000Z"
}
See Create an On-chain Integration.

Managing integrations

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

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

# Update watched addresses
curl -X PATCH http://localhost:3001/v1/onchain-integrations/ocint_01h... \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{ "watchedAddresses": ["0x1111...", "0x2222..."] }'

# Enable / disable
curl -X POST http://localhost:3001/v1/onchain-integrations/ocint_01h.../enable \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID"

curl -X POST http://localhost:3001/v1/onchain-integrations/ocint_01h.../disable \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID"
See List On-chain Integrations, Get On-chain Integration Detail, Update an On-chain Integration, Enable an On-chain Integration, and Disable an On-chain Integration.

Ingesting a transfer

POST /v1/onchain-integrations/{onchainIntegrationId}/ingest is how your poller or push-based indexer hands a discovered transfer to ReconLayer. The integration must be active, and the requested source mode must be enabled on the integration (pollingEnabled for source: "poll", pushEnabled for source: "push").
curl -X POST http://localhost:3001/v1/onchain-integrations/ocint_01h.../ingest \
  -H "Authorization: Bearer $RECONLAYER_API_KEY" \
  -H "x-organization-id: $ORG_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "poll",
    "txHash": "0xABCDEF1234567890abcdef1234567890ABCDEF1234567890abcdef12345678",
    "chainId": 137,
    "fromAddress": "0x1111111111111111111111111111111111111111",
    "toAddress": "0x2222222222222222222222222222222222222222",
    "tokenAddress": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
    "externalReference": "INV-2026-0099",
    "status": "confirmed",
    "amount": { "amount": "1000.00", "currency": "USDC" },
    "occurredAt": "2026-06-14T09:30:00Z",
    "payload": { "raw": "indexer transaction payload" }
  }'
source
string
default:"poll"
"poll" or "push" β€” determines which pollingEnabled/pushEnabled flag is checked, and the actor recorded on audit events (system:onchain_integration_poll or system:onchain_integration_push).
txHash
string
Transaction hash. Lowercased during normalization.
sourceRef
string
Either sourceRef or txHash is required, for deduplication.
chainId
number
Numeric chain ID, e.g. 137 for Polygon.
fromAddress
string
Sender address (lowercased).
toAddress
string
Recipient address (lowercased).
tokenAddress
string
Token contract address (lowercased), if applicable.
externalReference
string
Matched against PaymentIntent.externalReference.
status
string
default:"confirmed"
pending, confirmed, failed, reversed, or missing.
amount
object
required
{ "amount": string, "currency": string }.
paymentIntentId
string
Link directly to a known PaymentIntent.
references
array
Additional { type, value } FlowLegReference entries, merged with the auto-generated tx_hash/external_reference references.
metadata
object
Arbitrary metadata, merged with chainFamily: "evm" and networkId (from the integration).
payload
object
required
The raw transaction/event payload β€” stored verbatim on the RawRecord.
Response (OnchainIntegrationTransferIngestResponse):
{
  "networkFamily": "evm",
  "networkId": "137",
  "source": "poll",
  "evidence": {
    "accepted": true,
    "duplicate": false,
    "rawRecordId": "rr_01h...",
    "flowLegIds": ["fl_01h..."],
    "matchedCaseIds": ["case_01h..."],
    "signatureValid": null,
    "source": "api",
    "sourceRef": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef12345678"
  }
}
See Ingest On-chain Transfer Evidence.

EVM normalization details

Both the integration-ingest path and the direct POST /v1/evidence/onchain path call normalizeEvmTransfer, which:
  • Lowercases txHash, fromAddress, toAddress, and tokenAddress via hex normalization.
  • Produces a FlowLeg with type: 'onchain_transfer', phase: 'transfer'.
  • Builds FlowLegReferences for tx_hash (from the normalized txHash) and external_reference (from externalReference), merged with any explicit references you pass.
  • Stamps metadata.chainFamily: 'evm' always, and metadata.networkId (from the integration’s configured networkId) for integration-ingest β€” plus whatever custom metadata you provide.
The resulting RawRecord has source: 'api', sourceType: 'onchain_report', normalizedType: 'flow_leg', and normalizedId set to the normalized txHash (falling back to sourceRef if no txHash is present).

Next steps

  • Inbound Webhooks β€” the direct POST /v1/evidence/onchain path for one-off submissions.
  • Bridge Integration β€” Bridge webhooks can also produce onchain_transfer legs for ethereum/polygon rails.
  • Ingestion Flows β€” how on-chain evidence flows into the matching engine and ReconciliationCase updates.