Integration goal
Build a lender flow where:- The borrower is authenticated in lender UI.
- Lender backend calls Vault transfer launch endpoint.
- Lender frontend renders Vault embed URL in an iframe.
- Lender frontend listens to Vault lifecycle events via
postMessage.
Architecture overview
- Lender backend calls
POST /api/v1/loans/{loan_ref}/transfer-launch. - Vault returns launch artifacts:
embed_url,expires_at,token,asset_transfer_instruction_id. - Lender frontend loads
embed_urlin an iframe. - Vault iframe resolves token, starts provider transfer session, and emits lifecycle events to parent window.
Prerequisites
- Vault API reachable from lender backend (for example
http://34.170.194.254:8000) - Vault UI reachable from borrower browser (for example
http://34.170.194.254:5173in dev) - Lender backend has a valid Vault bearer token with scopes:
collateral:writeloans:read
- Lender frontend origin is known (for example
http://34.170.194.254:3000) and sent to Vault usingOriginheader
Backend launch call
Use Vault endpoint:POST /api/v1/loans/{loan_ref}/transfer-launch- Required scopes:
collateral:writeandloans:read - Required headers:
Origin: lender UI origin (exact scheme + host + port)Idempotency-Key: stable key for one launch action
- Path params:
loan_ref(required string)
- Required body fields:
asset_refquantity(number or string)custody_provider_code
- Expected response artifacts:
statusembed_url(includes token + parent origin context)expires_attokenasset_transfer_instruction_id
embed_url, expires_at, and token as launch artifacts that may be absent on non-ok responses. Lender code should fail when embed_url is missing.
The lender frontend should not call Vault transfer session APIs directly. It should only consume embed_url returned by lender backend.
Use asset_transfer_instruction_id as the correlation key for downstream lender/Vault workflows.
Example backend route (Node/Express)
Frontend iframe + event listener
RenderembedUrl in an iframe and consume only contract events from Vault:
vault.asset_transfer.readyvault.asset_transfer.completedvault.asset_transfer.errorvault.asset_transfer.closed
Example frontend component (React)
Event contract from Vault hosted widget
Vault posts messages to parent window using embed-session parent origin context.vault.asset_transfer.ready- payload: none
vault.asset_transfer.completed- payload:
{ status } statusvalues:pendingsucceededfailedsuccess(alias ofsucceeded)failure(alias offailed)unknown
- payload:
vault.asset_transfer.error- payload:
{ message }
- payload:
vault.asset_transfer.closed- payload: none
type as the stable contract and not parse provider-specific raw event streams.
Listener behavior requirements
- Validate
event.originagainst Vault UI origin (for examplehttp://34.170.194.254:5173) - Consume only documented contract event types
- Ignore unexpected/non-contract events
- Keep iframe mounted until
vault.asset_transfer.closed - Use
vault.asset_transfer.completedto capture transfer outcome - Do not advance lender workflow based on
closedalone - If
closedarrives beforecompleted, treat launch as cancelled/abandoned
Origin alignment checklist
To avoid origin-validation failures, keep these values aligned exactly (scheme + host + port):- Lender backend
Originheader on launch call must equal lender browser origin embed_url-derived parent origin should match lender frontend origin exactly- Lender frontend validates
event.originagainst Vault UI origin - Avoid mixing
localhostand127.0.0.1(different origins)
Origin and security requirements
- Never expose Vault bearer token in browser code.
Originheader on launch request must exactly match lender UI origin.- Validate
event.originagainst Vault UI origin in frontend listener. - Treat embed tokens as short-lived and one-time use.
- Re-launch for retries; never reuse stale
embed_urltokens.
Retry and lifecycle guidance
vault.asset_transfer.error generally appears in two categories:
- bootstrap/launch failures before provider flow is active (token/session/setup issues)
- provider-side failures before a completed outcome is observed
Correlation key
Useasset_transfer_instruction_id as the identifier for reconciliation across lender and Vault workflows.
Duplicate launch prevention
Vault embed tokens are single-use. Duplicate launch calls or duplicate iframe mounts can cause noisy logs and one-time token failures. Recommended lender-side controls:- implement single-flight guard per idempotency key
- disable launch UI while launch call is in-flight
- ensure framework lifecycle hooks cannot auto-launch multiple times
- render only one iframe per
embedUrl - never reuse old embed token/URL; request fresh launch on retry
- carry
X-Request-Idthrough logs for cross-system traceability
- one stable key per user launch action
- reuse only for retry of same action/payload
- generate a new key for each new user-initiated launch
Local testing checklist
- Start Vault API/UI and lender backend/frontend.
- Launch transfer from lender UI.
- Confirm iframe loads and emits expected event sequence.
- Confirm Vault runtime endpoints are invoked after iframe load.
- Confirm one-time token behavior and idempotent retry behavior.
- Confirm mismatched
Originis rejected.
Demo environment variables
Backend:VAULT_API_BASE_URL=http://34.170.194.254:8000VAULT_BEARER_TOKEN=<service-token-with-collateral-write-and-loans-read>LENDER_UI_ORIGIN=http://34.170.194.254:3000
VAULT_UI_ORIGIN=http://34.170.194.254:5173
Production hardening
- Add correlation IDs from lender request through Vault APIs for traceability
- Add retry/backoff only for session bootstrap network failures
- Provide graceful fallback UX if embed launch fails
- Instrument completion/error metrics by lender tenant and integration partner