Skip to main content

Etherfuse supports two approaches for KYC verification. Both work the same in sandbox as in production.
ApproachUse Case
UI-Based (Presigned URL)Quick integration, Etherfuse handles the verification UI
ProgrammaticWhite-label experience, custom UI, full control
KYC RequirementsRegardless of which approach you use, the following must be submitted for each customer:
  1. Selfie - A photo of the customer’s face (submitted by the customer or on their behalf)
  2. Government-issued identification - A valid ID document (passport, driver’s license, national ID, etc.)
  3. Proof of address - Document showing the customer’s current address (utility bill, bank statement, etc.)
If the government-issued ID includes the customer’s address (e.g., a driver’s license), it can satisfy both requirements. Otherwise, two separate documents must be submitted.After all data is submitted, Etherfuse reviews the information for accuracy before approving the customer.

UI-Based KYC Flow

The default approach uses Etherfuse’s hosted verification UI via presigned URLs.

Flow Overview

  1. Generate a presigned URL via POST /ramp/onboarding-url (this creates the customer if it doesn’t exist)
  2. Redirect the user to the presigned URL
  3. User completes identity verification and document upload in the Etherfuse UI
  4. Monitor status via kyc_updated webhooks or poll GET /ramp/customer/{customer_id}/kyc/{pubkey}

Example

Generate onboarding URL (creates customer):
POST /ramp/onboarding-url
{
  "customerId": "123e4567-e89b-12d3-a456-426614174000",
  "bankAccountId": "123e4567-e89b-12d3-a456-426614174001",
  "publicKey": "9Qx7r...",
  "blockchain": "solana"
}
Note: You generate the customerId and bankAccountId UUIDs. Etherfuse will create the customer and bank account records associated with your organization. Response:
{
  "presigned_url": "https://devnet.etherfuse.com/onboarding?token=..."
}
Redirect the user to presigned_url. They have 15 minutes to complete KYC verification and bank account linking in the Etherfuse-hosted UI.

Programmatic KYC Flow

Programmatic KYC allows partners to collect identity data in their own UI and submit it via API, bypassing the Etherfuse-hosted verification flow. This is ideal for partners who want to white-label the entire onboarding experience.

Flow Overview

1. Generate Presigned URL  POST /ramp/onboarding-url       (creates customer, returns auth token)
2. Submit Identity Data    POST /ramp/customer/{id}/kyc    (partner submits on user's behalf)
3. Upload Documents        POST /ramp/customer/{id}/kyc/documents
4. User Accepts Agreements POST /ramp/agreements/*         (user must authorize via presigned URL)
5. Monitor Status          Webhook: kyc_updated

Step 1: Generate Presigned URL

First, create the customer and obtain a presigned URL. This URL serves as the authentication mechanism for the end user throughout the onboarding process.
POST /ramp/onboarding-url
{
  "customerId": "123e4567-e89b-12d3-a456-426614174000",
  "bankAccountId": "123e4567-e89b-12d3-a456-426614174001",
  "publicKey": "9Qx7r...",
  "blockchain": "solana"
}
Response:
{
  "presigned_url": "https://devnet.etherfuse.com/onboarding?token=eyJhbGciOiJIUzI1NiIs..."
}
Save the presigned URL - You’ll need it for Step 4 when the user accepts agreements. The URL is valid for 15 minutes.

Step 2: Submit Identity Data

Collect the customer’s identity information and submit it via API. This can be done by the partner on behalf of the user.
POST /ramp/customer/{customer_id}/kyc
{
  "pubkey": "9Qx7r...",
  "identity": {
    "id": "9Qx7r...",
    "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"
      }
    ]
  }
}
Response:
{
  "status": "proposed",
  "message": "KYC data submitted for review"
}

Step 3: Upload Documents

Upload government ID and selfie images as data URLs. Upload ID Document (front and back):
POST /ramp/customer/{customer_id}/kyc/documents
{
  "pubkey": "9Qx7r...",
  "documentType": "document",
  "images": [
    {
      "label": "id_front",
      "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
    },
    {
      "label": "id_back",
      "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
    }
  ]
}
Upload Selfie:
{
  "pubkey": "9Qx7r...",
  "documentType": "selfie",
  "images": [
    {
      "label": "selfie",
      "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg..."
    }
  ]
}
Document Requirements:
  • Formats: JPEG, PNG
  • Max size: 10MB per image
  • Required: id_front, id_back (if applicable), selfie

Step 4: User Accepts Agreements

The end user must accept the required legal agreements before they can transact.
UI Flow vs Programmatic FlowIn the UI-based flow, these agreements are presented to and signed by the user automatically when they visit the presigned URL on the Etherfuse site.For programmatic KYC, you have two options:
  1. Redirect to Etherfuse - After submitting KYC data, redirect the user to the presigned URL to complete agreements on the Etherfuse site
  2. Accept via API - Call the agreement endpoints programmatically, ensuring the user has authorized the acceptance
If you choose to accept agreements via API, the presigned URL serves as the user’s authentication—it cryptographically proves the user’s identity and authorizes them to enter into binding agreements.
User Authorization RequiredThese agreements create legally binding obligations for the end user. The presigned URL authenticates the user and serves as their electronic signature authorization. You must ensure the actual end user (not just your system) initiates or explicitly authorizes these requests.
The following agreements must be accepted in order: 1. Electronic Signature Consent - User consents to conduct business electronically (E-Sign Act)
POST /ramp/agreements/electronic-signature
{
  "presigned_url": "https://devnet.etherfuse.com/onboarding?token=eyJhbGciOiJIUzI1NiIs..."
}
2. Terms and Conditions - User agrees to Etherfuse’s terms of service
POST /ramp/agreements/terms-and-conditions
{
  "presigned_url": "https://devnet.etherfuse.com/onboarding?token=eyJhbGciOiJIUzI1NiIs..."
}
3. Customer Agreement - User agrees to the terms governing on/off ramp transactions
POST /ramp/agreements/customer-agreement
{
  "presigned_url": "https://devnet.etherfuse.com/onboarding?token=eyJhbGciOiJIUzI1NiIs..."
}
When using programmatic KYC, the customerInfo field is optional since identity data was already submitted in Step 2.

Step 5: Monitor via Webhooks

Register a webhook for kyc_updated events to receive status updates.
POST /ramp/webhook
{
  "id": "your-webhook-uuid",
  "eventType": "kyc_updated",
  "url": "https://your-app.com/webhooks/kyc"
}
Webhook Payload (Approved):
{
  "kyc_updated": {
    "customerId": "123e4567-e89b-12d3-a456-426614174000",
    "walletPublicKey": "9Qx7r...",
    "approved": true,
    "updateReason": "KYC approved by admin"
  }
}
Webhook Payload (Rejected):
{
  "kyc_updated": {
    "customerId": "123e4567-e89b-12d3-a456-426614174000",
    "walletPublicKey": "9Qx7r...",
    "approved": false,
    "updateReason": "ID document is expired. Please submit a valid government-issued ID."
  }
}

Checking KYC Status

Query the current KYC status at any time (works for both approaches):
GET /ramp/customer/{customer_id}/kyc/{pubkey}
Response:
{
  "customerId": "123e4567-e89b-12d3-a456-426614174000",
  "walletPublicKey": "9Qx7r...",
  "status": "proposed",
  "onChainMarked": false,
  "currentRejectionReason": null,
  "selfies": [
    {
      "id": "selfie-123",
      "type": "selfie",
      "status": "proposed",
      "createdAt": "2024-01-01T00:00:00Z",
      "images": [
        { "imageURL": "https://...", "label": "selfie" }
      ]
    }
  ],
  "documents": [
    {
      "id": "doc-456",
      "type": "document",
      "status": "proposed",
      "createdAt": "2024-01-01T00:00:00Z",
      "images": [
        { "imageURL": "https://...", "label": "id_front" },
        { "imageURL": "https://...", "label": "id_back" }
      ],
      "extractedData": null
    }
  ],
  "currentKycInfo": {
    "id": "info-789",
    "email": "[email protected]",
    "phoneNumber": "+521234567890",
    "dateOfBirth": "1990-05-15",
    "name": {
      "givenName": "Juan",
      "familyName": "Garcia"
    },
    "address": {
      "street": "Av. Reforma 123",
      "city": "Mexico City",
      "region": "CDMX",
      "postalCode": "06600",
      "country": "MX"
    },
    "idNumbers": [
      { "value": "GAJU900515HDFRNN09", "type": "CURP" }
    ]
  },
  "approvedAt": null
}
Status Values:
StatusDescription
not_startedNo KYC data submitted
proposedData submitted, awaiting admin review
approvedKYC approved
approved_chain_deployingApproved, on-chain marking in progress (Solana only)
rejectedKYC rejected (see currentRejectionReason)

Handling Rejections

If KYC is rejected, the updateReason in the webhook (or currentRejectionReason in the status response) explains why. To resubmit:
  1. Address the rejection reason (e.g., upload a clearer document)
  2. Submit new data via the same endpoints
  3. The new submission creates a fresh review; old rejected data remains for audit purposes

Data Isolation (Programmatic Only)

When using programmatic KYC, partners can only access KYC data they submitted:
AccessorData Visibility
Partner (via API key)Only data where source_organization_id matches their org
Wallet OwnerAll data for their wallet
AdminAll data
This prevents partners from accessing PII submitted by other partners or directly by users.

Email Suppression (Programmatic Only)

When you submit KYC programmatically, Etherfuse suppresses standard KYC notification emails to the user. Your application is responsible for communicating status updates to users via the webhook events.