On-ramp integration guide
On-ramp integration guide
This guide walks through using Latitude’s on-ramp product. The examples cover two corridors: MXN → USDC via virtual CLABE (SPEI) and PHP → USDC via QR Ph (InstaPay). The flow is identical across corridors; only the KYC document type in Step 2 and the deposit account shape in Step 5 differ by country.
Before you start, make sure you’re familiar with the API basics — authentication, idempotency, webhooks, and prefunding.
Summary
The high-level steps are as follows:
- (Optional) Create a webhook subscription for
individualandconversionevents - Create an
individualthat represents the end user - Upload document images for KYC verification
- Wait for KYC result
- Create a
conversion_accountthat represents the on-ramp mechanism- The response contains a deposit account (virtual CLABE for MX, QR Ph payload for PH)
- Share the deposit instructions with the end user
- The user can now deposit fiat at any time
- User deposits fiat
- Receive webhooks for conversion lifecycle events
Detailed Steps
Step 1: (Optional) Create a Webhook Subscription
Create a webhook subscription to be notified of individual and conversion events. See API basics — Webhooks for details. The relevant event types for this flow are:
conversion.created— sent when a deposit is received into a virtual accountconversion.payout_initiated— sent when a blockchain transfer is initiatedconversion.completed— sent when a blockchain transfer is completedindividual.status_changed— sent when an individual’s status changes
Step 2: Create an Individual
The only field that differs between corridors here is document: MX uses a CURP, PH uses a TIN. All other fields (name, address, consent, etc.) have the same shape.
Mexico (MX)
Philippines (PH)
Request:
idv_consent_recorded_at is required for on-ramp: timestamp when the end user accepted identity verification consent (ISO 8601).
Response:
The response includes status and status_details. For on-ramp individuals, the initial status is action_required because document images are needed for KYC verification. The status_details array contains objects with a machine-readable code and a human-readable message explaining what’s needed.
Individual Statuses
When the status is action_required, status_details is an array of objects with code and message. When status is active, pending, or rejected, status_details is null.
Note:
status_details[].codeis a machine-readable string you can use for programmatic branching.status_details[].messageis a human-readable string suitable for manual review. New codes may be added over time without changing the set of statuses.
Step 3: Upload Verification Images
Note: Latitude supports other options for completing KYC. Contact us to discuss alternatives.
After creating the individual, upload photos of the front and back (if applicable) of the end user’s government ID.
Request:
The type field identifies what the image represents. The accepted types are document-front and document-back. The content field is the image as a base64-encoded data URI.
Response:
Replacing an Image
The endpoint uses merge-by-side semantics: only the sides included in the request are replaced. Existing images for other sides are preserved. For example, to replace just the front image:
Retrieving Image Metadata
Sandbox Image Testing
In sandbox, any valid image will generate an approved outcome by default. Use the following magic strings as the base64-encoded image content to trigger specific outcomes:
Sample request to simulate one image being blurry:
Step 4: Wait for KYC Result
After uploading document images, the individual’s status moves to pending while KYC verification is processed. You will receive an individual.status_changed webhook when the status changes.
- If the status changes to
active, proceed to create aconversion_account. - If the status changes to
action_required, checkstatus_details[].codeto determine what’s needed.
After taking the requested action (e.g., uploading clearer photos), the individual returns to pending and KYC verification is re-processed.
Sample individual.status_changed webhook (KYC passed):
Sample individual.status_changed webhook (action needed):
Step 5: Create a Conversion Account
The individual must have active status before a conversion account can be created.
The request is the same shape in both corridors; set source_currency to mxn for MX or php for PH. The deposit_account returned in the response differs by country — see the tabs below the request.
Request:
Response:
Mexico (MX) — virtual CLABE
Philippines (PH) — QR Ph
The deposit_account financial accounts are also associated with the individual and will appear in GET /v1/individuals/{id}.
Sharing deposit instructions with the end user
- MX: share the
clabestring with the end user. They can then send a SPEI transfer to that CLABE from any Mexican bank. - PH: the
qrph_payloadis an EMV QR Ph payload string conforming to theph.ppmi.p2m.qrphstandard. Render it as a scannable QR code in your UI using any standard QR code library (e.g.qrcode.js,zxing). The end user scans it with their bank or e-wallet app to pay via InstaPay.
Step 6: User Deposits Fiat
At this point, the end user may send funds to the deposit account at any time — via SPEI to the CLABE (MX), or by scanning the QR Ph code (PH). Each time a deposit is received, a new conversion will be created.
Simulate Deposit (Sandbox Only)
In sandbox, a deposit can be simulated using the sandbox-only simulation endpoint. Provide the financial_account_id of the deposit account and the amount of fiat currency to deposit. This endpoint works the same way for both corridors; just vary currency and the financial_account_id.
Request:
Note: Sandbox deposits are capped at a small amount per corridor (50 MXN for MX, 50 PHP for PH).
Step 7: Receive Webhooks
Note: Polling is also available as an alternative to webhooks. You can poll for updates on the list conversions endpoint.
The webhook event types and payload shapes are identical across corridors — only the currency fields in deposit and payout, and the numeric values, differ by country.

