Webhooks
How UR delivers business events to partner-registered HTTPS endpoints. Covers protocol, signatures, retries, idempotency, and the full event catalog.
This document is the canonical reference for partner backend engineers integrating with UR's webhook delivery service. It covers the wire protocol, signature verification, retry semantics, idempotency contract, and every event type currently in production.
If you only have a few minutes, read §1 Overview and §4 Signature verification. Everything else is reference material.
1. Overview
UR pushes asynchronous business events (transactions confirmed on-chain, KYC outcomes, allowance changes, card-authorization failures, partner-KYC session results) to an HTTPS endpoint you register in your partner contract.
The high-level contract:
Transport
HTTPS POST, Content-Type: application/json
Body
JSON envelope {event, data, timestamp}
Authentication
EIP-191 ECDSA signature in HTTP headers (V1 legacy + V2 recommended, both delivered in parallel)
Delivery semantics
At-least-once, ~48 h retry window, ±20 %-jittered exponential backoff
Success criterion
Your endpoint returns HTTP 2xx within the configured timeout (default 30 s)
Idempotency
We send X-Webhook-Request-Id (UUID v4); partners MUST dedupe on this
Ordering
Sequential per-partner; across partners is unordered
User-Agent:
URBank-Webhook/2.0(pre-v2.0 partners sawURBank-Webhook/1.0). Whitelist by domain, not UA. TheURBank-Webhookstring is a fixed wire-protocol constant retained for backward compatibility; it is not a product name.
2. Endpoint requirements
Your endpoint MUST:
Be reachable via HTTPS with a CA-issued certificate (no self-signed).
Accept
POSTwithContent-Type: application/json.Return HTTP 2xx on success within the configured timeout (default 30 s).
Return HTTP 4xx on permanent failure; we will NOT retry.
Return HTTP 5xx (or time out) on transient failure; we WILL retry.
Be idempotent: handle the same
X-Webhook-Request-Idmore than once gracefully (every retry reuses the same id).
Your endpoint SHOULD:
Verify at least one of the two signatures (see §4) before processing.
Reject any request whose
X-Webhook-Timestampskews more than ±5 minutes from your clock (replay protection).Ack fast (< 1 s ideal) and process asynchronously. A queue + worker pattern works best because our delivery worker holds a TCP connection open until you respond.
3. HTTP headers
Every outbound request carries these headers. X-Webhook-* are envelope metadata; X-Api-* are signature material.
Content-Type
application/json
Always JSON
User-Agent
URBank-Webhook/2.0
Stable for the v2.0 generation of the delivery service
X-Webhook-Request-Id
b3e8…-… (UUID v4)
Idempotency key. Same value across the first delivery and every retry of the same message. Dedupe on this.
X-Webhook-Timestamp
1735689600
Unix seconds at the moment the HTTP request is assembled
X-Webhook-Attempt
1, 2, …
1-indexed delivery attempt. First delivery is 1, first retry is 2.
X-Api-Signature
0x…
V1 (legacy) signature. Present whenever V1 is enabled in your contract.
X-Api-PublicKey
0x…
V1 signer Ethereum address. Same address as V2.
X-Api-Signature-V2
0x…
V2 (recommended) signature. Present whenever V2 is enabled.
X-Api-PublicKey-V2
0x…
V2 signer Ethereum address (= V1 address).
3.1 Response headers you can send
X-Webhook-No-Retry: true
On a 4xx or 5xx response when you've decided this message should never be retried
We move the message to DEAD immediately and stop retrying.
Everything else in your response is ignored. Status code is what we key on (see §5).
4. Signature verification
All webhooks are signed with the EIP-191 "personal sign" scheme used by Ethereum wallets:
The signing key is the same for V1 and V2; only the signed message differs.
4.1 V1 (legacy)
V1 is what legacy partners have always verified. It will remain on the wire indefinitely; a retirement announcement will run on a ≥3-month deprecation window if we ever do retire it.
4.2 V2 (recommended)
V2 binds the timestamp and request id into the signature. Without that binding an attacker who captures a webhook can replay the body with a fresh timestamp; with V2 the recovered address won't match. All new integrations should validate V2.
4.3 Address pinning
The signer address is published in your partner contract. Pin it on your side; do not trust whatever X-Api-PublicKey(-V2) says without comparing to the pinned value. The header is a convenience; the contract is the source of truth.
4.4 Reference implementation (Node.js)
The verifier call moved namespace between ethers major versions. Both forms are shown below; the rest of the function is identical.
ethers v6 (current; published 2023+):
ethers v5 (legacy; for partners on the v5 line):
If you're not sure which version you're on, run node -p "require('ethers').version". 6.x.x is v6; 5.x.x is v5. The hash + secp256k1 recovery underneath is the same EIP-191 algorithm in both versions, so the recovered address is byte-identical.
4.5 Reference implementation (Go)
4.6 Verifying both versions during the migration window
A partner who verifies V2 only and rejects on failure will break the day UR rotates a key (rare but planned). Defense-in-depth:
A common partner-side bug is parsing JSON first and then re-serializing for signature verification. That breaks because Go's encoding/json does not preserve key order. Read the raw request body byte-for-byte before deserializing. The req.rawBody / c.Request.GetBody() pattern in most frameworks does this for you.
5. Retry policy
5.1 Classification by response
HTTP 2xx
Success (message marked DELIVERED).
HTTP 4xx (except 408, 429)
Non-retryable (message marked DEAD).
HTTP 408, 429
Retryable (server-side hint that you were rate-limited or slow).
HTTP 5xx
Retryable.
Network error / timeout / DNS failure
Retryable.
HTTP response header X-Webhook-No-Retry: true
Treated as non-retryable regardless of status.
5.2 Backoff schedule
±20 % jittered exponential backoff, base 30 s, cap 6 h, max 20 attempts over roughly a 48-hour window:
1
(initial)
2
~30 s
3
~1 min
4
~2 min
5
~4 min
…
doubling each step, jittered
11
~4 h
12
~6 h (cap)
13 … 20
~6 h each
After attempt 20 the message is moved to DEAD and persisted for 90 days. UR operations can manually re-queue a DEAD message on partner request.
5.3 Circuit breaker
If we observe 20 consecutive failures (5xx / timeout / connection-refused) for a given partner, a per-partner circuit opens for 60 s. Open-circuit attempts are not sent to your endpoint; they're requeued for the next backoff slot. Your retry count is not incremented while the circuit is open.
5.4 What X-Webhook-Attempt tells you
The header is 1-indexed:
Attempt: 1: first delivery (fresh message)Attempt: 2+: retry
You can use this to bias logging (verbose on 1, terse on retries) or to detect partner-side hot loops where retries keep failing for the same root cause.
6. Event catalog
Subscription is per-partner, per-event-type. You only receive event types your contract subscribes to. Asking for an event you're not subscribed to is the most common silent-fail in integrations; if you don't see events you expect, check your subscription before debugging anything else.
New event types may be added; existing event-type bodies are append-only. We may add new optional fields, never rename or remove existing ones.
6.0 Field-naming quirks (read this first)
The customer identifier appears under one of two field names depending on event type, and its JSON type also varies between events. These are wire-fixed across all delivered events; partners must handle both shapes.
transaction, transaction_v2, card_spending_failed
urId
string
allowance
urId
int64 (number, no quotes)
fma.account.result, fma.penny_drop.result, fma.kyc.reuse_check.result, fma.kyc.result, fma.additional_kyc.required, fma.additional_kyc.completed
urId
int64 (number, no quotes)
kyc_status, sumsub_kyc_result, monthly_limit_exceeded
tokenId
string
All four shapes refer to the same UR NFT token id. The split is legacy: the tokenId family predates the unification on urId, and the int64/string split predates the unification on string. The wire shapes are frozen for backwards compatibility; do not assume future events will pick one canonical form.
Other quirks worth flagging up-front:
Subscription changes (enable / disable an event type, rotate webhook URL) are coordinated with your UR technical liaison; there is no self-service API.
Token symbols referenced in event bodies (
USD24,EUR24,CHF24,JPY24,CNH24,SGD24,HKD24,USDC) are the UR-issued tokenized fiat assets; see Tokenized deposits for the catalogue and contract addresses.Wire-fixed JSON keys such as
deadLine(capitalL, incard_spending_failed) are preserved verbatim from legacy schemas. Where they appear, the field reference table for that event notes the quirk explicitly.
6.1 Money movement & on-chain events
transaction (legacy transaction event)
Emitted when a tracked transaction is confirmed on-chain (deposit, withdrawal, transfer, FX, card spend).
Bidirectional fan-out: for a P2P transfer between two of your customers (both bound to your partner_id) you receive two webhooks: one with direction=out and the sender's urId, and one with direction=in and the receiver's urId. For a self-transfer (same urId on both sides) business-key dedup collapses to a single webhook.
Field reference (see also TransactionData in the API reference):
urId
string
Token ID of the customer this event belongs to
title, subtitle, image
string
UI strings; partner can ignore or display
amount, currency
string
Display amount (decimal string) and ticker; partner-visible
type
string
Business type (e.g. transfer_in, transfer_out, card_spend, fx)
direction
string
in or out from this customer's perspective
txHash, chainId
string
On-chain identifiers; chainId is CAIP-2 (e.g. eip155:5000)
inputToken, inputAmount, inputTokenAddress
string
Source asset
outputAmount
string (omitempty)
FX leg
mcc
uint64 (omitempty)
Merchant Category Code (card spend)
reference, bankAccount, crdMultiToken, partnerRefId, …
various (omitempty)
Channel-specific extras
transaction_v2 (current transaction event)
Same business event as transaction, but a normalized schema and lifecycle (broadcastTimeE9, finalTimeE9). New partners should subscribe to transaction_v2 and ignore transaction.
refundType
string
Refund discriminator. "" (not a refund), BANK_REFUND (bank transfer refund), CARD_REFUND (card spending refund), or CARD_REVERSAL (card authorization reversal). See §12.4 Identifying refund transactions for details.
detailsJson is a stringified JSON blob whose structure depends on data.type. See detailsJson reference by transaction type for the full schema of each type.
allowance (ERC-20 allowance change)
Emitted when one of your customer's allowance flags flips (approve / revoke / transferFrom drains it).
data.urId
int64
Customer UR id (note: int64 here, not string; legacy quirk)
data.allowances[]
array
Updated allowance rows
…tokenSymbol
string
One of USD24, EUR24, CHF24, JPY24, CNH24, SGD24, HKD24, USDC
…hasAllowance
bool
True iff on-chain allowance > 0 for the UR contracts
…chainId
string
CAIP-2 chain id
Only the rows that changed are present; never a full snapshot. Treat allowances as a patch, not a state dump.
card_spending_failed (card authorization rejected for user-caused reason)
Emitted when a customer's card spend fails because of insufficient balance / missing allowance / similar customer-actionable reason. Not for network or processor failures.
urId
string
Customer UR id
status
string
Always FAILED for this event
reason
string (omitempty)
Machine-readable reason code
transactionCurrency, transactionAmount
string (omitempty)
Original authorization details
cardCurrency
string (omitempty)
Card's billing currency
deadLine
string (omitempty)
Customer-action deadline (ISO 8601). Wire-fixed: the JSON key is literally deadLine (capital L), not the more conventional deadline. Preserved verbatim for backwards-compat and will not change.
authorizationToken
string (omitempty)
Reference for the rejected authorization
monthly_limit_exceeded (monthly spending limit hit)
Emitted when a customer's monthly spend in CHF would cross their configured limit.
tokenId
string
Customer UR id
txAmount, txCurrency
string
Transaction that triggered the breach
availableCHF, usedCHF, maxCHF
string
Limit accounting in CHF
6.2 KYC (generic)
kyc_status (KYC status change)
Emitted when UR returns an updated compliance status for a customer.
tokenId
string
Customer UR id
status
string
One of: Pending, Pass, Rejected, ManualReview, Error
sumsub_kyc_result (Sumsub review result)
Emitted when a Sumsub KYC review completes for one of your customers.
tokenId
string
Customer UR id
kycResult
string
Pass or Rejected
6.3 Partner-managed (FMA) KYC
These events fire only for partners using the Fiat-Mediated Account (FMA) integration model: partners who own the KYC user-experience and let UR run downstream compliance. They are not sent to non-FMA partners.
The FMA events share a common envelope shape (per-event exceptions are noted in each entry):
urId(int64): UR token id (issued by UR-side mint aftercreate-account)sessionId(string, UUID): the partner-KYC session this event belongs to (present onfma.account.result,fma.penny_drop.result,fma.kyc.reuse_check.result,fma.additional_kyc.completed; absent onfma.kyc.resultand onfma.additional_kyc.required, which fires before the retry session exists)partnerId(string): your partner id (same string you sign requests with)status(string): a state discriminator inside one logical event family (thefma.additional_kyc.*pair does not carry it: the event name itself is the discriminator)occurredAt(int64): Unix seconds at emission (thefma.additional_kyc.*pair usescreatedAtandcompletedAtinstead)
fma.account.result (account onboarding outcome)
Single event that subsumes "account activated" and "session rejected" outcomes. Branch on status:
status
Meaning
Extra fields
activated
KYC passed end-to-end. Customer is now Live on-chain with their CHF spending limit set.
(none)
rejected
The session ended in failure (compliance hard-rejected, KYC-rejected, draft expired, or retry expired).
rejectCode, rejectReason
rejectCode enumeration (the values you will see in production, grouped by what caused the rejection):
rejectCode
Meaning
Origin
KYC_REJECTED
Compliance review returned a hard reject. Partner should NOT retry.
KYC pipeline
UPSTREAM_RETRY_EXHAUSTED
We could not reach an upstream (Sumsub or the compliance backend, etc.) within the 48-hour retry budget. Partner may request ops re-queue.
KYC pipeline
INTERNAL_ERROR
Defensive catch-all for a bug on our side. Always paired with an incident on UR's side; contact ops.
KYC pipeline
SUMSUB_REJECTED
Sumsub returned a hard reject during identity verification.
KYC pipeline
FORMA_NOT_SIGNED
Customer never signed Form A within the session lifetime.
KYC pipeline
SESSION_EXPIRED
Draft session expired (never made it past the data-ingestion step within its TTL).
Maintenance job
PENNY_TIMEOUT
Session reached IdentityVerification but neither penny-drop nor NFC completed within 30 days.
Maintenance job
STALE_ORPHAN_GC
Garbage-collection of orphaned legacy session rows; should not appear for newly-onboarded partners.
Maintenance job
Additional Reject* codes from the API-level error catalogue may appear if a partner-side API call triggers a session termination (rare). Treat any unknown code as "session ended; surface rejectReason to your support tooling and ask UR ops for translation". The set above covers >99 % of real production rejections.
When you receive status=activated you can trust:
The customer's UR NFT is Live on-chain
Their
setClientLimittransaction has confirmedIt is safe to allow card spends / withdrawals / etc.
fma.penny_drop.result (penny-drop verification)
Per-attempt verification result for the penny-drop flow (applies to partners whose KYC variant requires a micro-transfer match step; subscription is per partner). Branch on status:
status
Meaning
verified
The customer's micro-transfer matched our expected amount; KYC advances to IdentityVerification → SignFormA.
failed
The most recent attempt did not match. The customer can retry up to a configured cap. When the last allowed attempt fails, the payload includes exhausted: true and no further attempts will be accepted.
Field reference (every field below is present on every delivery of this event type unless marked optional):
urId
int64
always
UR token id
sessionId
string (UUID)
always
Partner-KYC session id
partnerId
string
always
Your partner id
status
string
always
verified or failed
amount
object {value: string, currency: string}
always
The micro-transfer amount we observed (decimal string + 3-letter ISO currency)
remitterCountryISO3
string
always
The remitter person's country, ISO 3166-1 alpha-3. Used by partner UI for compliance display.
failReason
string
status=failed only
Machine-readable reason. Common values: AMOUNT_MISMATCH, AMOUNT_BELOW_MIN, CURRENCY_MISMATCH, IBAN_MISMATCH. Treat unknown values as "failed; ask UR ops for translation".
attemptsRemaining
int
status=failed only
How many additional attempts the customer has after this one. 0 means no more attempts will be accepted by /kyc/penny-transfer.
exhausted
bool
status=failed only
true iff attemptsRemaining=0. When true, the customer must switch to the NFC fallback path; the penny-drop endpoint will return PENNY_DROP_MAX_ATTEMPTS_EXCEEDED for any further calls. The session itself is not terminated; an fma.account.result may still follow after the NFC path completes.
transactionRecordId
int64
always
ur_transaction_records.id of the evaluated on-chain fiat-deposit mint. Stable identity for the deposit this result refers to.
txHash
string
always
On-chain tx hash of the evaluated fiat-deposit mint.
txLogIndex
int64
optional
Event ordinal inside the tx; disambiguates when a single tx hash carries multiple mint events. Omitted when not applicable.
occurredAt
int64
always
Unix seconds at emission
fma.kyc.reuse_check.result (shared-token handoff verdict)
Fires only for partners using the shared-token (Copy Applicant) KYC reuse model. It is the async mirror of a handoff attempt's conclusive verdict: emitted once per conclusive attempt for status ∈ {passed, incomplete}. Terminal (non-remediable) verdicts are deliberately NOT webhooked; the partner learns those from the synchronous handoff response / POST /api/fma/v1/kyc/check and UR ops. A lost delivery degrades to polling /kyc/check, so this event is fire-and-forget on our side.
urId
int64
always
UR token id
partnerId
string
always
Your partner id
sessionId
string (UUID)
always
The handoff session this verdict belongs to
attempt
int
always
Session-scoped monotonic attempt counter (part of the business key, so redeliveries of the same attempt dedupe)
status
string
always
passed (import accepted) or incomplete (remediable; resubmit the missing data and re-handoff)
correlationId
string
always
Trace id for cross-referencing with UR ops
missingFields
string[]
status=incomplete only
Dot-path identifiers of the data still required
requiredLevels
string[]
optional
Cloned-workflow level names mapped from missingFields, when resolvable
occurredAt
int64
always
Unix seconds at emission
fma.kyc.result (Sumsub review outcome for shared-token flows)
Fires for shared-token / pull / SDK FMA flows when the underlying Sumsub review reaches an outcome. This is the Sumsub-layer KYC verdict, distinct from fma.account.result (which is the account-activation outcome). Branch on status.
status
Meaning
pass
Identity data accepted; the flow has advanced past all Sumsub levels to SignFormA.
rejected
Sumsub returned a hard reject (RED).
step_up
Additional documents requested. (Reserved; not emitted by this path yet.)
urId
int64
always
UR token id
partnerId
string
always
Your partner id
status
string
always
pass, rejected, or step_up
rejectCode
string
status=rejected only
Machine-readable reject reason
rejectReason
string
optional
Free-form reason text for support tooling
occurredAt
int64
always
Unix seconds at emission
Note:
fma.kyc.resultcarries nosessionId; correlate onurId+occurredAt.
fma.additional_kyc.required (ops triggered a partner-driven retry)
Emitted when UR operations decide an already-onboarded customer must resubmit part or all of their KYC. UR seals the customer's current session as it emits this event, so any sessionId you still hold stops working. The new session is not in the payload: you create it by calling POST /api/fma/v1/kyc/session/create, which returns its id.
Field reference:
directiveId
string (UUID)
Identifies this retry request. Correlates with the completed event and with UR support tickets.
type
string
Always retry today; other values are reserved.
taskType
string
What the customer must redo, and the field to branch on: full, passport, address_recheck, or redo_form_a.
retryLevel
int
The retry category ops selected. Advisory; taskType is authoritative. Note this is a different quantity from the retryLevel returned by /kyc/session/current, which counts sessions.
fiat24Mode
string
auto_register or ops_offline. Decides whether you finish the session with POST /kyc/submit.
dataChannel
string
push, sdk, or shared-token. Matches your integration path.
partnerId
string
Your partner id
externalUserId
string
The partner-side user id you bound to this urId at create-account time
urId
int64
UR token id (unchanged between the original and retry sessions)
retryOfSessionId
string (UUID)
The session this retry replaces. It is already sealed; calls against it return SESSION_NOT_FOUND (30006).
retryReason
string
Free-form reason text suitable for partner-side support tooling (not for direct end-user display)
requiredFields
string[]
Push channel only: dot-path identifiers to correct, for example registerRequest.address.street. Empty on other channels.
deadlineAt
int64
Unix seconds, or 0 when unset. Informational only: UR does not expire the directive or session when it passes, and no event fires at the deadline.
createdAt
int64
Unix seconds when the retry was triggered
Action on receipt: call POST /api/fma/v1/kyc/session/create to claim the new session, then drive the customer through the steps taskType names. See Retry KYC for the per-channel walkthrough.
fma.additional_kyc.completed (retry session finished)
Emitted when the retry session reaches its last step. No further call is required from you.
directiveId, taskType, retryLevel, and fiat24Mode repeat the values from the required event, so the pair matches without a lookup. Two fields differ:
sessionId
string (UUID)
The retry session that just completed, the same id /kyc/session/create returned to you
completedAt
int64
Unix seconds when the session completed
Subscribe to both
fma.additional_kyc.requiredandfma.additional_kyc.completed. An unsubscribed event is dropped, so a partner subscribed to only one of the pair silently sees half the flow.
7. Idempotency and ordering
7.1 What to dedupe on
Dedupe on X-Webhook-Request-Id. That UUID is fixed for the lifetime of a message: every retry sends the same id, and our database uniqueness constraint on (event_type, partner_id, business_key) guarantees the same logical event never gets two request ids.
Concretely on your side:
7.2 What NOT to dedupe on
The HTTP body, or any subset of fields inside it. Two different business events (e.g. status
activatedand a later statusrejectedfor the same session id after a retry flow) can share field values, and you do want to receive both.X-Webhook-Timestamp: that changes on every retry.
7.3 Ordering guarantees
Per-partner first-delivery order: the first delivery attempts for messages belonging to one partner go out in the same order they were produced. The internal Kafka topic is partitioned by
partner_idand a single consumer goroutine per partition processes strictly sequentially, advancing the Kafka offset as soon as each delivery attempt completes (whether success or failure).Retries are out-of-band: a failed message moves to
PENDING_RETRYand is re-published to the same partition by a separate retry dispatcher after its backoff. The consumer does not block on retries; it proceeds to the next fresh message immediately. As a consequence, a retry of message A may arrive after a later message B if B's first delivery happens to succeed while A is still cycling. Build your handler so that re-applying message A after message B's effects is safe.Across partners: no ordering guarantee.
Within an FMA session: the
fma.account.resultevent for asessionIdis the final word for that session; once you've seen it, no further events for the samesessionIdwill arrive. Earlierfma.penny_drop.resultevents for the samesessionIdare produced first, but the retry caveat above still applies (a delayed retry could theoretically arrive afteraccount.result). If you order on the partner side byoccurredAt, you'll get the intended chronology even on a delayed retry.
8. Operations
8.1 Rate limits
Per-partner default: 20 requests per second, burst 40. If you can sustain higher and need it, raise it with your UR technical liaison.
8.2 IP allowlists
Our outbound IP is stable per Kubernetes deployment but may shift on node rotation. Allowlist by domain, not by IP. If you must allowlist by IP, ask for the current allocation in writing and treat it as advisory; we won't notify before rotations.
8.3 TLS
Standard CA chain. We don't pin certificates on our side; you don't need to share anything for handshake.
8.4 Recovery probing
During incident response our system may re-drive delivery to your endpoint, but it does not send a synthetic probe event; there is no __probe__ event type. Recovery reuses the actual business messages (replayed from the internal Kafka topic), so anything you receive is a real, dedupable event carrying its normal X-Webhook-Request-Id. Keep handling every delivery idempotently (see §7); there is no special probe body to special-case.
8.5 Cancellation
UR operations can cancel a PENDING_RETRY message via internal admin tooling (for example, if a refund / reversal makes an earlier event obsolete). Messages already in flight may still arrive at your endpoint for a few seconds after cancellation. Treat late arrivals idempotently; your business-side reconciliation should already handle this.
9. Troubleshooting
Signature verification fails on every webhook
You're re-serializing the body after JSON-parse
Read req.rawBody (or framework equivalent); sign over raw bytes, not re-encoded
Some webhooks verify, others don't
Two different keys delivered (V1 vs V2 with different message composition); you're using one verifier for both
Implement both verifiers (see §4.6)
Webhooks for one customer suddenly stop
Your endpoint is returning non-2xx and we hit the 20-attempt cap → DEAD
Check your logs at X-Webhook-Request-Id; ask UR ops to re-queue
Webhooks for ALL customers stop briefly, then resume
Circuit breaker opened on our side after 20 consecutive failures from your endpoint
Look at the 60 s before resumption for the failure mode
Same X-Webhook-Request-Id arrives 5× in a minute
Your endpoint is timing out (we keep retrying) or returning 5xx
Acknowledge fast; process asynchronously
Webhook count is roughly half what you expected
Subscription not enabled for the missing event type
Check your partner contract subscriptions
FMA activated arrives but customer is not Live on-chain
Should never happen; activated is only fired after the on-chain status flip and limit-set both confirm
Capture the X-Webhook-Request-Id and sessionId, contact UR ops
For unresolved incidents, open a ticket via your UR technical liaison with at minimum:
X-Webhook-Request-IdX-Webhook-TimestampX-Webhook-Event-TypeThe full request body (raw bytes)
The HTTP status you returned and the response body
10. Quick reference card
Verification message for V2:
Return 200 OK to ack. Return 4xx to fail permanently. Return 5xx / time out to retry.
See also
Signature and verify: generic UR request-signing rules and the signature scheme for the partner → UR direction. Note: that doc currently documents only the V1 webhook signature (UR → partner direction); the V2 scheme defined in §4.2 of this page is the authoritative webhook reference. The EIP-191 primitive is shared across both directions, but the message composition differs (request-body + deadline for partner → UR; timestamp + request-id + body for V2 webhooks).
OpenAPIs: full reference for the partner REST API;
TransactionDataand friends are documented there.Partner integration flow concepts: when each event fires in the customer lifecycle.
The user lifecycle: end-to-end customer journey, with webhook touchpoints.
Last updated