Onboarding a business? Use POST /ramp/organization to create a child organization, then have Etherfuse KYB-approve it. KYB-approved orgs are fully compliant — no individual KYC, document uploads, or agreement signing required. The flow below is for onboarding individual customers.
Supported blockchains: The
blockchain field accepts solana, stellar, base, polygon, or monad. All examples use Solana, but the flow works identically across chains.Two types of auth are used throughout this flow:
- Steps 1–5 use your API key with the
customer_idin the URL path - Step 6 uses the presigned URL as the authentication token (no API key)
Flow
Create Customer Organization — POST /ramp/organization
Show Details
Show Details
Create a child organization for your customer. You can optionally include wallets and a bank account to set everything up in a single call. See POST /ramp/organization for the full schema.Response:
curl -X POST https://api.sand.etherfuse.com/ramp/organization \
-H "Authorization: <api_key>" \
-H "Content-Type: application/json" \
-d '{
"id": "<customer_uuid>",
"displayName": "Customer Name",
"wallets": [
{
"publicKey": "<wallet_public_key>",
"blockchain": "solana"
}
]
}'
{
"organizationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"displayName": "Customer Name",
"wallets": [
{
"id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"publicKey": "9Qx7r...",
"blockchain": "solana"
}
],
"bankAccount": null
}
Business customers? If Etherfuse KYB-approves the child organization, it is fully compliant — skip the remaining steps. The KYC, document, agreement, and presigned URL steps below are only needed for individual customers.
Submit Identity Data — POST /ramp/customer/{customer_uuid}/kyc
Show Details
Show Details
Submit the customer’s identity information via your API key. The
pubkey field is the wallet public key (not a UUID). See POST /ramp/customer/{id}/kyc for the full schema.curl -X POST https://api.sand.etherfuse.com/ramp/customer/<customer_uuid>/kyc \
-H "Authorization: <api_key>" \
-H "Content-Type: application/json" \
-d '{
"pubkey": "<wallet_public_key>",
"identity": {
"id": "<wallet_public_key>",
"email": "[email protected]",
"phoneNumber": "+521234567890",
"occupation": "Software Engineer",
"name": {
"givenName": "Juan",
"familyName": "Garcia"
},
"dateOfBirth": "1990-05-15",
"address": {
"street": "Av. Reforma 123",
"city": "Mexico City",
"region": "CDMX",
"postalCode": "06600",
"country": "MX"
},
"idNumbers": [
{ "value": "GAJU900515HDFRNN09", "type": "CURP" },
{ "value": "GAJU9005156V3", "type": "RFC" }
]
}
}'
Show Required fields reference
Show Required fields reference
When the customer signs their agreement (Step 6), all required fields must be present — either from this endpoint or via the
If any required field is missing at agreement signing, the request returns a
customerInfo field on the customer agreement.| Field | Required | Path in identity payload |
|---|---|---|
| First Name | Yes | name.givenName |
| Last Name | Yes | name.familyName |
| Phone Number | Yes | phoneNumber |
| Yes | email | |
| Occupation | Yes | occupation |
| Address | Yes | address (object: street, city, region, postalCode, country) |
| Date of Birth | No | dateOfBirth |
| CURP | Mexico only | idNumbers entry with type: "CURP" |
| RFC | Mexico only | idNumbers entry with type: "RFC" |
400 indicating which field was not provided.idNumbers is optional — defaults to empty. Only needed for Mexican customers (CURP and RFC). Non-Mexican customers can omit it entirely.Upload Documents — POST /ramp/customer/{customer_uuid}/kyc/documents
Show Details
Show Details
Upload government ID and selfie images as base64-encoded data URLs. See POST /ramp/customer/{id}/kyc/documents for the full schema.Upload ID Document (front and back):Upload Selfie:Requirements: JPEG or PNG, max 10MB per image. Labels:
{
"pubkey": "<wallet_public_key>",
"documentType": "document",
"images": [
{ "label": "id_front", "image": "data:image/jpeg;base64,/9j/4AAQ..." },
{ "label": "id_back", "image": "data:image/jpeg;base64,/9j/4AAQ..." }
]
}
{
"pubkey": "<wallet_public_key>",
"documentType": "selfie",
"images": [
{ "label": "selfie", "image": "data:image/jpeg;base64,/9j/4AAQ..." }
]
}
id_front, id_back (if applicable), selfie.In sandbox, you can submit any placeholder image — the content isn’t validated.
Register Bank Account — POST /ramp/customer/{customer_uuid}/bank-account
Show Details
Show Details
Register the customer’s Mexican bank account (CLABE) using your API key. The Business Account:
Valid country codes: GET /lookup/country-codes
account field accepts two variants — the system auto-detects Personal vs Business based on the fields present. See POST /ramp/customer/{id}/bank-account for the full schema.Personal Account:{
"account": {
"transactionId": "<uuid>",
"firstName": "Juan",
"paternalLastName": "Garcia",
"maternalLastName": "Lopez",
"birthDate": "19900515",
"birthCountryIsoCode": "MX",
"curp": "GAJU900515HDFRNN09",
"rfc": "GAJU9005156V3",
"clabe": "012345678901234567"
}
}
{
"account": {
"transactionId": "<uuid>",
"name": "Garcia Enterprises S.A. de C.V.",
"countryIsoCode": "MX",
"incorporatedDate": "20150101",
"rfc": "GEN150101ABC",
"clabe": "098765432109876543"
}
}
Show Bank account field reference
Show Bank account field reference
| Field | Personal | Business | Format |
|---|---|---|---|
transactionId | Required | Required | UUID (you generate) |
firstName | Required | — | |
paternalLastName | Required | — | |
maternalLastName | Required | — | |
birthDate | Required | — | YYYYMMDD |
birthCountryIsoCode | Required | — | ISO 3166-1 alpha-2 (e.g. MX) |
name | — | Required | Legal entity name |
incorporatedDate | — | Required | YYYYMMDD |
countryIsoCode | — | Required | ISO 3166-1 alpha-2 (e.g. MX) |
curp | Required | — | 18 characters |
rfc | Required | Required | Personal: 13 chars, Business: 12 chars |
clabe | Required | Required | Exactly 18 digits |
Alternative: Presigned URL auth — You can also register bank accounts via
POST /ramp/bank-account using the presigned URL instead of your API key. This is the path used by the Etherfuse hosted onboarding UI. See POST /ramp/bank-account for details.Generate Presigned URL — POST /ramp/onboarding-url
Show Details
Show Details
Generate a presigned URL to use as the authentication token for agreement signing. The Response:
customerId must match the organization ID from Step 1. See POST /ramp/onboarding-url for the full schema.curl -X POST https://api.sand.etherfuse.com/ramp/onboarding-url \
-H "Authorization: <api_key>" \
-H "Content-Type: application/json" \
-d '{
"customerId": "<customer_uuid>", # From Step 1
"bankAccountId": "<bank_account_uuid>", # You generate this UUID
"publicKey": "<wallet_public_key>",
"blockchain": "solana"
}'
{
"presigned_url": "https://devnet.etherfuse.com/onboarding?token=eyJhbGciOiJIUzI1NiIs..."
}
Save the presigned URL — you’ll need it for the next step. Valid for 15 minutes.
KYB-approved orgs can skip this step. If the child organization from Step 1 has been KYB-approved by Etherfuse, no presigned URL or agreement signing is required.
Accept Agreements
Show Details
Show Details
KYB-approved orgs can skip this step. If the child organization from Step 1 has been KYB-approved by Etherfuse, agreements are handled as part of the KYB process.
User Authorization Required — These create legally binding obligations. The presigned URL authenticates the user and serves as their electronic signature authorization. Ensure the actual end user initiates or explicitly authorizes these requests.
POST /ramp/agreements/electronic-signature
{ "presignedUrl": "<presigned_url_from_step_5>" }
POST /ramp/agreements/terms-and-conditions
{ "presignedUrl": "<presigned_url_from_step_5>" }
POST /ramp/agreements/customer-agreement
{ "presignedUrl": "<presigned_url_from_step_5>" }
customerInfo:{
"presignedUrl": "<presigned_url_from_step_5>",
"customerInfo": {
"phone": "+521234567890",
"email": "[email protected]",
"occupation": "Software Engineer",
"additionalInfo": {
"curp": "GAJU900515HDFRNN09",
"rfc": "GAJU9005156V3"
}
}
}
customerInfo fields merge with whatever identity data is already on file. Only include what’s missing.In sandbox, the customer is auto-approved when the customer agreement is signed.
Monitor Status
Show Details
Show Details
Track onboarding progress via See Checking KYC Status for response details and status values.
kyc_updated webhooks or by polling GET /ramp/customer/{id}/kyc/{pubkey}:GET /ramp/customer/{customer_uuid}/kyc/{pubkey}
Show Bank account compliance
Show Bank account compliance
A bank account must be
compliant: true before you can create orders. Two paths to compliance:- Customer self-verifies — After submitting data via API, redirect the customer to the presigned URL. They complete identity verification (facial scan + ID matching) in the Etherfuse UI, which marks the bank account compliant immediately.
- Etherfuse admin reviews — Submit everything programmatically and wait for admins to review the uploaded documents. The customer’s selfie and government ID (Step 3) must be uploaded before the admin can complete the compliance check.
In sandbox, all bank accounts under an approved customer are considered compliant.
Show Data isolation
Show Data isolation
Partners can only access KYC data they submitted:
This prevents partners from accessing PII submitted by other partners or directly by users.
| Accessor | Data Visibility |
|---|---|
| Partner (via API key) | Only data where source_organization_id matches their org |
| Wallet Owner | All data for their wallet |
| Admin | All data |
Show Email suppression
Show Email suppression
When you submit KYC programmatically, Etherfuse suppresses standard KYC notification emails to the user. Your application is responsible for communicating status updates via webhook events.