API basics
This page covers the shared fundamentals that apply across all Latitude API products.
Base URL
All API requests are made to:
Authentication
Latitude uses HMAC-SHA256 signature-based authentication. All API requests must include a properly formatted Authorization header containing the signature and metadata.
Required Headers
Authorization Header Components
Example: Properly Signed Request
Request:
Expected Headers:
Canonical Request Payload
A plaintext representation of the request, consisting of the following parts joined by newlines:
- Request method
- Request path
- Query string — omitted entirely (no line, not a blank line) when the request has no query string
- Formatted headers (
name:value, lowercased names), sorted by name, one per line - A sorted list of signed header names, delimited by semicolons
- The SHA256 hash of the request payload
There are no blank separator lines between the parts.
Example (a POST with no query string, so the query string line is absent):
Signature Calculation
The final signature is generated by:
- Producing a SHA256 hash of the canonical request payload
- Adding the resulting hash to the final payload, and producing a HMAC-SHA256 signature using the secret key
- Encoding the final signature using Base64
Final Payload:
Final Signature:
Base64 Encoded Signature:
Idempotency
The Transfers (POST /v1/transfers) and Conversion Accounts (POST /v1/conversion_accounts) endpoints support idempotency. This allows you to safely retry requests without accidentally performing the same operation twice. To use it, include an Idempotency-Key header with a UUID value. We recommend a V4 UUID to avoid collisions.
When you supply a valid Idempotency-Key, we save the resulting status code and body and return it for subsequent requests with the same key. When we return a saved result, we include an Idempotency-Replayed: true header in the response. Successful responses (200 and 201) are cached; error responses are not, so a request that failed can be retried with the same key.
If the same key is sent with a different request body, the API will return a 409 Conflict error. A key that is not a valid 36-character UUID is rejected with a 400 Bad Request.
Note: Cached responses expire after 24 hours. We recommend generating a new UUID for each unique operation and storing it alongside your internal records so retries use the same key.
Webhooks
Subscribe to webhook events to get real-time notifications about transfer status changes, conversion updates, and KYC results. This is the recommended way to track the lifecycle of your payments.
To create a subscription, send a POST request to /v1/webhook_subscriptions with the URL where you’d like to receive events and the event types you’re interested in.
Request:
Available Event Types
Note: Webhook subscriptions are created at the organization level, not per-individual or per-transfer. Once a subscription is active, you will receive events for all individuals, transfers, and conversions across your account.
Event Type Payload Examples
Every webhook is delivered as a POST request with a JSON body of the form below. The event_type field identifies the event, and event_payload contains the resource snapshot at the time the event fired.
The shape of event_payload depends on the resource the event relates to: transfer events carry a transfer object, conversion events carry a conversion object, and individual.status_changed carries an individual status object. Realistic examples for each event type follow.
transfer.created
Fired when a transfer is created. For transfers funded via a crypto deposit, the payload includes deposit_instructions describing where to send funds.
transfer.funded
Fired once funds are received and the transfer begins processing.
transfer.completed
Fired when the payout settles successfully. completed_in (seconds) and settlement_reference_id are populated.
transfer.failed
Fired when a transfer terminally fails during processing. failure_reason describes the cause.
transfer.expired
Fired when a transfer’s funding deadline passes before funds are received. The transfer never started processing.
conversion.created
Fired when a deposit is received into a conversion account’s virtual account. The blockchain payout has not yet been initiated, so payout timestamps and tx_hash are null.
conversion.payout_initiated
Fired when the blockchain payout to the destination wallet is broadcast. initiated_at and tx_hash are populated; completed_at is still null.
conversion.completed
Fired when the blockchain payout confirms. completed_at is populated.
conversion.failed
Fired when a conversion terminally fails.
individual.status_changed
Fired when an individual’s status changes (for example, as a result of KYC review). status_details lists any action codes with human-readable messages. When there are no details, the status_details key is omitted from the payload entirely rather than being sent as an empty array or null.
Possible status values are pending, action_required, under_review, active, and rejected.
Prefunded Flows
Currently all Latitude flows are prefunded. You send funds to Latitude and those funds settle before you can execute transfers. You can prefund using:
- USD via wire transfer to your Latitude-managed bank account
- USDC via blockchain deposit on the Base network
You can find the instructions for funding your account in the dashboard. We will support more flexible and real-time funding flows in the future.
Sandbox Environment
Latitude provides a sandbox environment for testing your integration without moving real money. Contact us to get sandbox API credentials.
The sandbox supports magic strings for simulating different outcomes (e.g. KYC approval/rejection, transfer success/failure). See the individual integration guides for sandbox-specific details.

