This guide walks you through the complete process of converting USD to Bitcoin and sending it to a Spark wallet using just-in-time (JIT) funding.
Prerequisites
Before starting this guide, ensure you have:
- A Grid API account with valid authentication credentials
- Access to the Grid API endpoints (production or sandbox)
- A webhook endpoint configured to receive notifications
- A Spark wallet address where the Bitcoin will be delivered
Overview
The on-ramp process consists of the following steps:
- Create a customer via the API
- Create an external account for the destination Spark wallet
- Create a quote for the USD-to-BTC conversion with current exchange rate
- Fund the quote using the provided payment instructions (JIT funding)
- Receive webhook notification confirming Bitcoin delivery to the Spark wallet
Step 1: Customer Onboarding
If your platform is a regulated financial institution that already has a KYC/KYB process in place, you can create a customer directly via the API. Unregulated platforms can onboard customers either through the hosted KYC/KYB link flow or by submitting customer data directly through the API — for INDIVIDUAL customers (KYC), personal information goes through POST /customers; for BUSINESS customers (KYB), you also register beneficial owners via POST /beneficial-owners.
Regulated Platforms
Unregulated Platforms
Regulated platforms have lighter KYC requirements since they handle compliance verification internally.
The KYC/KYB flow allows you to onboard customers through direct API calls.Regulated financial institutions can:
- Direct API Onboarding: Create customers directly via API calls with minimal verification
- Internal KYC/KYB: Handle identity verification through your own compliance systems
- Reduced Documentation: Only provide essential customer information required by your payment counterparty or service provider.
- Faster Onboarding: Streamlined process for known, verified customers
Creating Customers via Direct API
For regulated platforms, you can create customers directly through the API without requiring external KYC verification:To register a new customer in the system, use the POST /customers endpoint:The examples below show a more comprehensive set of data. Not all fields are strictly required by the API for customer creation itself, but become necessary based on currency and UMA provider requirements if using UMA. Individual customer
Business Customer
Unregulated platforms rely on Grid to run KYC for individuals and KYB for businesses. You can onboard customers either through the hosted KYC/KYB link flow below, or by submitting customer data directly through the API. Both paths produce the same kycStatus transitions and emit the same CUSTOMER.KYC_APPROVED / CUSTOMER.KYC_REJECTED / CUSTOMER.KYC_PENDING (and CUSTOMER.KYB_* equivalents) webhooks.
Either path works for unregulated platforms:
- Hosted flow: Redirect customers to a Grid-hosted link (or embed the provider SDK) for identity verification. Best when you want Grid to handle the entire collection UX.
- Direct API onboarding: Collect customer information in your own UI and submit it via the API. For
INDIVIDUAL customers (KYC), personal information goes through POST /customers. For BUSINESS customers (KYB), you also register beneficial owners via POST /beneficial-owners. Submit for review with POST /verifications.
Hosted KYC Link Flow
The hosted KYC flow provides a secure, hosted interface where customers can complete their identity verification and onboarding process.The flow is two steps: create the customer with the information you have, then generate a hosted KYC link for that customer. The customer’s kycStatus stays PENDING until they complete the hosted flow.1. Create the customer
Create the customer with POST /customers, supplying at least customerType and any fields you already have. See Configuring Customers for the full list of optional pre-fill fields.Persist the returned id (the Grid customer ID) — you’ll need it for the next step.2. Generate a KYC link
Response:The response always includes kycUrl for the hosted flow. For providers that support direct SDK integration (currently SUMSUB), a token is also returned — you can pass this to the provider’s web SDK to embed verification in your own UI instead of redirecting. Both paths update the customer’s kycStatus identically.
Complete KYC Process
Create the customer
Call POST /customers with customerType and any pre-fill fields you have. The returned id is the customer’s Grid ID; their kycStatus is PENDING until verification completes.
Verify contact channels (only where required)
Some regions (for example, the EU) require the customer’s email and/or phone to be verified before KYC can begin. When this applies, the customer carries a contactVerification object listing only the channels that are required — for example { "email": "PENDING", "phone": "PENDING" }, or just { "email": "PENDING" } if a provider requires email alone. When the object is absent, no contact verification is required and you can skip this step.For each channel that’s present, drive a send-then-confirm pair (the provider delivers a one-time code by email/SMS):
POST /customers/{customerId}/verify-email, then POST /customers/{customerId}/verify-email/confirm with { "code": "..." }
POST /customers/{customerId}/verify-phone, then POST /customers/{customerId}/verify-phone/confirm with { "code": "..." }
Re-POST the send endpoint to resend a code. Each channel moves PENDING → VERIFIED. (verify-email/verify-phone return 409 for a channel the provider doesn’t require.)Generating the KYC link before every present channel is VERIFIED returns 409.
Generate the KYC link
Call POST /customers/{customerId}/kyc-link. Each call returns a fresh single-use kycUrl and expiresAt; previously-issued links remain single-use but aren’t invalidated.The redirectUri you pass is embedded in the generated kycUrl and is used to automatically return the customer to your application after they complete verification.
Send the customer through verification
Redirect the customer to kycUrl, or — if you want to embed the flow directly — initialize the provider’s SDK with the returned token.The hosted URL is single-use and expires at expiresAt. If a customer needs to retry, call the endpoint again to generate a new link.
Track the decision
Reaching your redirectUri only means the customer finished the hosted flow — not that they were approved. Wait for the final decision in one of two ways:
- Webhook (recommended): Subscribe to
CUSTOMER.KYC_APPROVED / CUSTOMER.KYC_REJECTED (and CUSTOMER.KYB_APPROVED / CUSTOMER.KYB_REJECTED for business customers) to be notified when the customer reaches a terminal status. CUSTOMER.KYC_PENDING (and the KYB_PENDING sibling) also fires when the customer is submitted for review — subscribe to it as well if you want to surface an “under review” state to the customer.
- Polling: Call
GET /customers/{customerId} and inspect kycStatus.
Handle completion
On APPROVED, the customer is ready to transact — proceed with account setup and unlock funding. On REJECTED, surface the appropriate next step (for example, regenerate the link or request manual review).
Direct API Onboarding
Prefer to collect identity information in your own UI and submit it to Grid yourself? Use the API directly instead of redirecting to a hosted link. The customer’s kycStatus transitions the same way and you receive the same CUSTOMER.KYC_APPROVED / CUSTOMER.KYC_REJECTED / CUSTOMER.KYC_PENDING (and CUSTOMER.KYB_* equivalents) webhooks.The shape of the flow depends on the customer type:
- KYC (
INDIVIDUAL customers) — supply the customer’s personal information through the customer endpoint. No beneficial owners are involved.
- KYB (
BUSINESS customers) — create the business customer, then register its beneficial owners, directors, and officers individually.
KYC (individual)
KYB (business)
Create the customer with personal information
Call POST /customers with customerType: INDIVIDUAL and the personal information collected from the customer (legal name, date of birth, address, nationality, etc.). The returned id is the customer’s Grid ID; kycStatus starts at PENDING.Where the customer’s provider requires it (for example, EU customers), the customer carries a contactVerification object and their email and phone must be VERIFIED before you submit for verification. Drive each channel with the send-then-confirm pairs (verify-email / verify-email/confirm and verify-phone / verify-phone/confirm) described in the Hosted flow’s “Verify email and phone” step. When contactVerification is absent, no contact verification is required.
Upload supporting documents (if requested)
Some jurisdictions or currencies require an ID document or proof of address. Upload them with POST /documents using multipart/form-data, referencing the customer by customerId. Submit for verification
Call POST /verifications to submit the customer for review. The response includes a verificationStatus. If anything is missing, verificationStatus is RESOLVE_ERRORS and the errors array describes exactly what to collect before retrying.Submitted successfully:Blocked by missing data: Track the decision
Track terminal kycStatus transitions via the CUSTOMER.KYC_APPROVED / CUSTOMER.KYC_REJECTED webhook (recommended) or by polling GET /customers/{customerId}. CUSTOMER.KYC_PENDING also fires when the customer is submitted for review — subscribe to it if you want to surface an “under review” state. On APPROVED, unlock funding and money movement.
When Grid runs KYB for a business customer, the following information and documents are collected before onboarding completes. Use this list to plan what to gather from the business — provide it via POST /customers, POST /beneficial-owners, and POST /documents (or through the hosted flow).
- Entity full legal name
- Doing Business As (DBA) name, if applicable
- Physical address — the principal place of business, local office, or other physical location of the entity opening the account
- Countries of operation
- Identification number — U.S. taxpayer identification number, or, for a foreign business without one, alternative government-issued documentation certifying the existence of the business
Ownership and control structure
Collected for:
- One control person — a single individual with significant responsibility to control, manage, or direct the legal entity, and
- All beneficial owners — every individual who owns 25% or more of the legal entity, directly or indirectly.
For every such individual, provide:
- Full name
- Date of birth
- Address
- Identification number, by residency:
- U.S. persons — Social Security Number (SSN) or Individual Taxpayer Identification Number (ITIN)
- Non-U.S. persons — one or more of: ITIN, passport number with country of issuance, alien identification card number, or another government-issued document evidencing nationality or residence and bearing a photograph or similar safeguard
Required documents
- Company formation and existence documents. For example:
- Certificate of incorporation
- Articles of association
- Proof of ownership and control structure. For example:
- Corporate organization and ownership chart
- Shareholder agreements
- Operating agreements
- Register of members
- Certification of controlling person and beneficial owners
- Proof of address, dated within the last 3 months. For example:
- Utility bill
- Bank statement
- Lease agreement
- Official correspondence
- Tax ID or equivalent identifying-number documents
- For non-U.S. beneficial owners — passport plus one additional government-issued ID. For example:
Create the business customer
Call POST /customers with customerType: BUSINESS and the business information (legal name, registration number, tax ID, registered address). Register beneficial owners
Register each beneficial owner, director, or officer with POST /beneficial-owners. Each one is verified individually with their own personal information. Upload supporting documents
Upload business documents (registration, articles of incorporation) and any documents required for individual beneficial owners with POST /documents. Reference the company by customerId and each owner by beneficialOwnerId. Submit for verification
Call POST /verifications to submit the business for review. The response includes a verificationStatus. If beneficial owners, fields, or documents are missing, verificationStatus is RESOLVE_ERRORS and the errors array tells you exactly what to collect — note that resourceId may point at either the customer or a specific beneficial owner.Blocked by missing data:Submitted successfully: Track the decision
Track terminal kycStatus transitions via the CUSTOMER.KYB_APPROVED / CUSTOMER.KYB_REJECTED webhook (recommended) or by polling GET /customers/{customerId}. CUSTOMER.KYB_PENDING also fires when the business is submitted for review — subscribe to it if you want to surface an “under review” state. On APPROVED, unlock funding and money movement.
Step 2: Create an External Account for the Destination Wallet
Register the customer’s Spark wallet as an external account so it can be used as a quote destination.
Request
Response
External accounts can be reused across multiple quotes. Register once, then reference by ID.
Step 3: Create a Quote for Fiat-to-Crypto Conversion
Create a quote to convert USD to Bitcoin and deliver it to the Spark wallet. The quote will provide the current exchange rate and payment instructions for funding.
Request
Response
The quote shows:
- Sending: $100.00 USD (including $2.50 fee)
- Receiving: 0.00083333 BTC (83,333 satoshis)
- Exchange rate: 8.3333 sats per USD cent (~$120,000 per BTC)
- Quote expires: In 5 minutes
- Payment instructions: Bank account details and reference code for funding
For JIT-funded quotes, do NOT call the /quotes/{quoteId}/execute endpoint.
Simply fund using the payment instructions, and Grid will automatically
execute the conversion upon receiving your payment. The execute endpoint is
used for quotes with an internal account or pullable external account as the source.
Step 4: Fund the Quote (Just-in-Time)
In production, you would initiate a real-time push payment (ACH, RTP, wire, etc.) to the bank account provided in paymentInstructions, making sure to include the exact reference code RAMP-ABC123 in the transfer memo.
In Sandbox, you can simulate funding using the /sandbox/send endpoint:Response: In production, your customer or platform would initiate a bank transfer to the provided account details, include the reference code RAMP-ABC123 in the transfer memo, and wait for Grid to detect the incoming payment (typically 1-3 business days for ACH, instant for RTP/wire). You’ll receive a webhook notification when Bitcoin is delivered to the Spark wallet.
The reference code is critical for matching your payment to the quote. Always
include it exactly as provided in the payment instructions.
Step 5: Receive Completion Webhook
Once Grid receives your payment and completes the USD-to-BTC conversion and delivery, you’ll receive a webhook notification:
The customer now has 83,333 satoshis (0.00083333 BTC) in their Spark wallet!
Summary
You’ve successfully completed a fiat-to-crypto on-ramp! Here’s what happened:
- ✅ Created a customer via API
- ✅ Registered the destination Spark wallet as an external account
- ✅ Created a quote with exchange rate and payment instructions
- ✅ Funded the quote using JIT payment (simulated in sandbox)
- ✅ Bitcoin automatically converted and delivered to Spark wallet
Next Steps