A Typescript library for interacting with the Spritz Finance API
npm install --save @spritz-finance/api-client yarn add @spritz-finance/api-clientGet started with Spritz in minutes:
import {
SpritzApiClient,
Environment,
PaymentNetwork,
BankAccountType,
BankAccountSubType,
DebitCardNetwork,
AmountMode,
} from '@spritz-finance/api-client'
// Initialize the client with your integration key
const client = SpritzApiClient.initialize({
environment: Environment.Sandbox,
integrationKey: 'YOUR_INTEGRATION_KEY_HERE',
})
// Create a user
const user = await client.user.create({
email: 'user@example.com',
})
// Set the user's API key
client.setApiKey(user.apiKey)
// Add a bank account
const bankAccount = await client.bankAccount.create(BankAccountType.USBankAccount, {
accountNumber: '123456789',
routingNumber: '987654321',
name: 'My Checking Account',
ownedByUser: true,
subType: BankAccountSubType.Checking,
})
// Create a payment request
const paymentRequest = await client.paymentRequest.create({
amount: 100,
accountId: bankAccount.id,
network: PaymentNetwork.Ethereum,
})
// Get transaction data for blockchain payment
const transactionData = await client.paymentRequest.getWeb3PaymentParams({
paymentRequest,
paymentTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC on mainnet
})
// Use transactionData to execute blockchain transaction in your app- Quick Start
- Installation
- API Overview
- Usage
- User Management
- Payment Flow
- Accounts
- Account Management
- Bill Institutions
- Payment Requests
- Payments
- Onramp Payments
- Webhooks
- On-ramp Setup Guide
Purpose: As an integrator, this guide will assist you in creating users and performing user-specific operations on the Spritz platform using the provided API key.
When you create a user using your integration key:
- You will receive an
API keyspecific to that user. - This enables you to interact with the Spritz platform on the user's behalf.
Using the user-specific API key, you can:
- Identity Verification: Guide a user through the identity verification process.
- Account Addition:
- Add Bills for the user.
- Register Bank accounts.
- Issue Virtual cards.
- Payment Requests: Initiate payment requests to the aforementioned accounts.
- Blockchain Transactions: Issue blockchain-based transactions to fulfill the payment requests.
- Payment Status: Query the status of payments directed to the user's accounts.
Your integration key is provided by Spritz and must always be provided. The api key is specific to each user, and is returned once the user is created. Leave the api key blank if you haven't created the user yet.
import { SpritzApiClient, Environment } from '@spritz-finance/api-client'
const client = SpritzApiClient.initialize({
environment: Environment.Sandbox,
apiKey: 'YOUR_USER_API_KEY_HERE',
integrationKey: 'YOUR_INTEGRATION_KEY_HERE',
})To create a new Spritz user, all you need is the user's email address. Note that trying to create a user with an email that already exists in the Spritz platform will throw an error.
const user = await client.user.create({
email: 'bilbo@shiremail.net',
})
// Response
user = {
email: "bilbo@shiremail.net"
userId: "62d17d3b377dab6c1342136e",
apiKey: "ak_ZTBGDcjfdTg3NmYtZDJlZC00ZjYyLThlMDMtZmYwNDJiZDRlMWZm"
}After creating a user, you can easily set the user's API key onto your initialized client using the provided method:
client.setApiKey(user.apiKey)Now you're ready to issue requests on behalf of the user.
There is a scenrio where you may need to get access to a users API key again. This can happen if you are trying to sign in a user that already has a Spritz account, or if you have lost access to their API key. In this case, you can reauthorize the user by providing their email. The process is that we will send the user an OTP code to their email, and then the user must pass that code on to you to confirm that they are allowing you to interact with their account on their behalf.
const { success } = await client.user.requestApiKey('bilbo@shiremail.net')
const { apiKey, userId, email } = await client.user.authorizeApiKeyWithOTP({
email: 'bilbo@shiremail.net',
otp: '123456',
})Use this to fetch the user's basic data
const userData = await client.user.getCurrentUser()Purpose: To ensure users are properly identified before interacting with the Spritz platform.
All users must undergo basic identity verification before they can engage with the Spritz platform's features.
-
User Creation: Upon the creation of a new user, their default verification status will be set to
NotStarted. -
Checking Verification Status: The user's verification data is included in the
getCurrentUserresponse, including verification status, verification URL, verified country, and retry capability. -
Verification Transition: Once a user completes the identity verification process, their status will change from
NotStartedtoVerified. Only then can the user fully interact with the platform. -
Getting Verification URL: The verification URL is included in the user data response and is essential for the user to proceed with their identity verification.
Spritz offers two methods for integrating KYC verification, both using the getVerificationParams() method:
// Get verification parameters from Spritz
const verificationParams = await client.user.getVerificationParams()
// The response includes:
// - inquiryId: Unique identifier for this verification inquiry
// - verificationUrl: URL for hosted verification
// - sessionToken: Token for use with Plaid Link SDK
// - verificationUrlExpiresAt: Expiration timestamp for the verification URLUse the verificationUrl from getVerificationParams() for a straightforward integration where Spritz handles the entire verification flow:
const verificationParams = await client.user.getVerificationParams()
const verificationUrl = verificationParams.verificationUrl
const expiresAt = verificationParams.verificationUrlExpiresAt
// Important: The verification URL is short-lived and single-use
// - Check the expiration with verificationUrlExpiresAt before using
// - Once accessed, the URL becomes invalid
// - If verification is not completed, call getVerificationParams() again for a new URL
// Use the verification URL in your application:
// - Browser: Open the URL in a new browser tab
// - In-App: Embed the URL in an iframe within your application
// - Mobile: Open the URL in a native mobile web view
// - Email: Send users an email containing the linkNote: The verification URL can only be used once. If the user doesn't complete verification after accessing the URL, you'll need to call getVerificationParams() again to generate a fresh URL and session.
For more control over the verification experience, use the inquiryId and sessionToken (if present) from getVerificationParams() with the Persona Embedded Flow:
const verificationParams = await client.user.getVerificationParams()
const inquiryId = verificationParams.inquiryId
const sessionToken = verificationParams.sessionToken // May be null for some flows
// Use the inquiryId (and sessionToken if present) with Persona's Embedded Flow
// to create a custom verification experience
// This allows you to:
// - Customize the UI/UX of the verification process
// - Handle callbacks and events directly
// - Integrate verification seamlessly into your application flow
// - Build a native mobile experience using Persona's mobile SDKsThe embedded flow method is ideal when you want to:
- Maintain full control over the user experience
- Integrate verification directly into your app without redirects
- Handle verification events and callbacks programmatically
- Track and resume verification sessions with the inquiryId
See the Persona Embedded Flow documentation for detailed integration instructions with the inquiryId and sessionToken.
When a user's verification fails, Spritz now provides additional metadata to help understand the failure reason and provide better user experience. This metadata is particularly useful for handling duplicate identity scenarios.
The verification metadata is available when a verification has failed and includes:
failureReason: The specific reason why the verification faileddetails.matchedEmail: For duplicate identity failures, shows the email of the matched user (only if created through the same integration)
The following verification failure reasons may be returned:
verify_sms: SMS verification faileddocumentary_verification: Document verification failedrisk_check: Risk assessment check failedkyc_check: KYC (Know Your Customer) check failedwatchlist_screening: Watchlist screening check failedselfie_check: Selfie verification check failedaddress_invalid: Provided address is invalidduplicate_identity: Identity already exists in the system
When a verification fails due to duplicate_identity, the system detects that the user has already been verified under a different account. The matchedEmail field helps determine:
- If
matchedEmailis populated: The duplicate user was created through your integration - If
matchedEmailisnull: The duplicate user was created through a different integration (e.g., the main Spritz app)
This allows you to provide appropriate guidance to your users:
const userData = await client.user.getCurrentUser()
if (userData.verificationMetadata?.failureReason === 'duplicate_identity') {
const matchedEmail = userData.verificationMetadata.details.matchedEmail
if (matchedEmail) {
// User exists within your integration
console.log(`This identity is already verified with account: ${matchedEmail}`)
// Guide user to use their existing account
} else {
// User exists in Spritz but not in your integration
console.log('This identity is already verified with another Spritz account')
// Inform user they need to use their original Spritz account
}
}Execute a payment in a few simple steps:
- Select an Account: Choose the account you wish to pay to.
- Initiate Payment Request: Use the account's ID, your desired payment amount, and the chosen blockchain network to create a payment request.
- Retrieve Transaction Data: Use the
getWeb3PaymentParamsmethod to obtain the necessary transaction data for fulfilling the payment request. - Blockchain Transaction: Issue the required blockchain transaction from the user's wallet.
- Payment Confirmation: After blockchain transaction confirmation, check the status of the TradFi payment.
For Spritz to process a TradFi payment to an account, we need to receive a blockchain transaction on our smart contract, which provides us the crypto funds. As an integrator, it's essential to manage how the blockchain transaction is initiated from the user's wallet to Spritz.
- Wallet Apps: If your application functions as a wallet, prompt the user to sign a transaction using data from
getWeb3PaymentParams. - Web-based Dapps: Use your existing connection to the user's wallet to prompt a transaction.
If your application doesn't have a connection to the user's wallet, consider implementing one. Some popular options include:
// Fetch all bank accounts for the user
const bankAccounts = await client.bankAccount.list()
// Choose a bank account to use for the payment request
const account = bankAccounts[0]
// Create a payment request for the selected bank account
const paymentRequest = await client.paymentRequest.create({
amount: 100,
accountId: account.id,
network: PaymentNetwork.Ethereum,
})
// Retrieve the transaction data required to issue a blockchain transaction
const transactionData = await client.paymentRequest.getWeb3PaymentParams({
paymentRequest,
paymentTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC on mainnet
})
/**
* Issue blockchain transaction with the transaction data
* and wait for confirmation
**/
// Retrieve the payment issued for the payment request to check the payment status and confirmation
const payment = await client.payment.getForPaymentRequest(paymentRequest.id)Spritz emphasizes its capabilities in account handling and payment processing.
Spritz supports four distinct types of accounts:
- Bank Account
- Debit Card
- Bill
- Virtual Card
Though each account type possesses its unique creation process and specific properties, it's important to understand that all of them are uniformly termed as an "account" within the Spritz platform.
- Common Properties: Every type of account shares certain properties consistent across the platform.
- Unique Properties: Each account type also has attributes specific to its nature and functionality.
Recognizing these nuances is crucial for optimal interaction with the Spritz platform's account-related features.
Spritz offers a dedicated interface to manage bank accounts, allowing seamless listing and addition of bank account details for users.
To retrieve all bank accounts linked to a user:
const bankAccounts = await client.bankAccount.list()The bankAccount.list() method returns an array of user-linked bank accounts, complete with essential details to display in a UI and facilitate payments:
const bankAccounts = [{
id: "62d17d3b377dab6c1342136e",
name: "Precious Savings",
type: "BankAccount",
bankAccountType: "USBankAccount",
bankAccountSubType: "Checking",
userId: "62d17d3b377dab6c1342136e",
accountNumber: "1234567",
bankAccountDetails: {
routingNumber: "00000123",
}
country: "US",
currency: "USD",
email: "bilbo@shiremail.net",
institution: {
id: "62d27d4b277dab3c1342126e",
name: "Shire Bank",
logo: "https://tinyurl.com/shire-bank-logo",
},
ownedByUser: true,
createdAt: "2023-05-03T11:25:02.401Z",
deliveryMethods: ['STANDARD', 'INSTANT']
}]Currently, Spritz supports the addition of US bank accounts:
The input structure for adding a US bank account is defined as:
// Input arguments for creating a US bank account
export interface USBankAccountInput {
accountNumber: string
email?: string | null
name?: string | null
ownedByUser?: boolean | null
routingNumber: string
subType: BankAccountSubType
}import { BankAccountType, BankAccountSubType } from '@spritz-finance/api-client'
const bankAccounts = await client.bankAccount.create(BankAccountType.USBankAccount, {
accountNumber: '123456789',
routingNumber: '987654321',
name: 'Precious Savings',
ownedByUser: true,
subType: BankAccountSubType.Savings,
})Currently, Spritz supports the addition of Canadian bank accounts:
The input structure for adding a Canadian bank account is defined as:
// Input arguments for creating a Canadian bank account
export interface CABankAccountInput {
accountNumber: string
email?: string
name: string
ownedByUser?: boolean | null
transitNumber: string
institutionNumber: string
subType: BankAccountSubType
}import { BankAccountType, BankAccountSubType } from '@spritz-finance/api-client'
const bankAccounts = await client.bankAccount.create(BankAccountType.CABankAccount, {
accountNumber: '123456789',
transitNumber: '12345',
institutionNumber: '123',
name: 'Precious Savings',
ownedByUser: true,
subType: BankAccountSubType.Savings,
})Spritz provides support for adding debit cards as payment accounts, allowing users to make payments directly to their debit cards.
To retrieve all debit cards linked to a user:
const debitCards = await client.debitCard.list()The debitCard.list() method returns an array of user-linked debit cards:
const debitCards = [
{
id: '62d17d3b377dab6c1342136e',
type: 'DebitCard',
name: 'My Visa Debit',
userId: '62d17d3b377dab6c1342136e',
country: 'US',
currency: 'USD',
payable: true,
debitCardNetwork: 'Visa',
expirationDate: '12/25',
cardNumber: '4111111111111111',
mask: '1111',
createdAt: '2023-01-01T00:00:00Z',
paymentCount: 5,
externalId: 'ext-123',
},
]To add a new debit card for a user:
import { DebitCardNetwork } from '@spritz-finance/api-client'
const debitCard = await client.debitCard.create({
name: 'My Visa Debit',
cardNumber: '4111111111111111',
expirationDate: '12/25',
})The input structure for adding a debit card is:
export interface DebitCardInput {
name?: string | null // Optional name for the card
cardNumber: string // Full card number (13-19 digits)
expirationDate: string // Expiration date in MM/YY format
}Supported debit card networks:
VisaMastercard
Spritz provides robust support for bills, allowing seamless management and interaction with user billing accounts. Below is a guide to the methods and functionalities specifically designed for handling bills within Spritz.
To retrieve all bill accounts associated with a user:
const bills = await client.bill.list()The bill.list() method returns an array of user-associated bills, complete with essential details for display in a UI and for processing payments:
const bills = [
{
id: '62d17d3b377dab6c1342136e',
name: 'Precious Credit Card',
type: 'Bill',
billType: 'CreditCard',
userId: '62d17d3b377dab6c1342136e',
mask: '4567',
originator: 'User',
payable: true,
verifying: false,
billAccountDetails: {
balance: 240.23,
amountDue: 28.34,
openedAt: '2023-05-03T11:25:02.401Z',
lastPaymentAmount: null,
lastPaymentDate: null,
nextPaymentDueDate: '2023-06-03T11:25:02.401Z',
nextPaymentMinimumAmount: 28.34,
lastStatementBalance: 180.23,
remainingStatementBalance: null,
},
country: 'US',
currency: 'USD',
dataSync: {
lastSync: '2023-05-03T11:25:02.401Z',
syncStatus: 'Active',
},
institution: {
id: '62d27d4b277dab3c1342126e',
name: 'Shire Bank Credit Card',
logo: 'https://tinyurl.com/shire-bank-logo',
},
createdAt: '2023-05-03T11:25:02.401Z',
deliveryMethods: ['STANDARD'],
},
]Currently, Spritz allows the addition of US bill accounts only. The process involves identifying the institution managing the bill and inputting the bill's account number. Here's a guide on how to add a bill for a user:
import { BillType } from '@spritz-finance/api-client'
const institutions = await client.institution.popularUSBillInstitutions(BillType.CreditCard)
const billInstitution = institutions[0]
const accountNumber = '12345678913213'
const bill = await client.bill.create(billInstitution.id, accountNumber, BillType.CreditCard)Spritz offers the ability to create virtual cards that users can fund using cryptocurrency. These virtual cards represent an alternative payment account offered by Spritz. To effectively interact with the Virtual Card feature, use the API endpoints detailed below.
The fetch endpoint returns an object containing details associated with the virtual card. Importantly, this object excludes sensitive card information such as the card number and the CVV.
const virtualCard = await client.virtualCard.fetch()const virtualCard = {
id: '62d17d3b377dab6c1342136e',
type: 'VirtualCard',
virtualCardType: 'USVirtualDebitCard',
userId: '62d17d3b377dab6c1342136e',
mask: '0001',
country: 'US',
currency: 'USD',
balance: 0,
renderSecret: 'U2FsdGVkX18bLYGYLILf4AeW5fOl8VYxAvKWVDtbZI5DO7swFqkJ2o',
billingInfo: {
holder: 'Bilbo Baggins',
phone: '+123456789',
email: 'bilbo@shiremail.net',
address: {
street: '1 Bagshot Row',
street2: '',
city: 'Hobbiton',
subdivision: 'The Shire',
postalCode: '12345',
countryCode: 'ME',
},
},
}import { VirtualCardType } from '@spritz-finance/api-client'
const virtualCard = await client.virtualCard.create(VirtualCardType.USVirtualDebitCard)To show the sensitive card details that users require for payment transactions, you must integrate our dedicated drop-in widget. This widget securely renders card details. Use the renderSecret, obtained from the standard fetch card endpoint, in conjunction with the user's API key.
We currently support and maintain the following packages for the card rendering process:
Each account created in Spritz is allocated a unique on-chain payment address for each network. Tokens transferred to this address will be picked up and credited to the account. A list of the addresses, one per network, is available under the paymentAddresses property. Refer to the Spritz UI for which tokens are accepted for these addresses -- generally, we accept at least USDC and USDT on all networks which we integrate with.
[
{
id: '62d17d3b377dab6c1342136e',
name: 'Precious Credit Card',
type: 'Bill',
billType: 'CreditCard',
...
paymentAddresses: [
{
network: 'ethereum',
address: '0xc0ffee254729296a45a3885639AC7E10F9d54979',
},
{
network: 'polygon',
address: '0xc0ffee254729296a45a3885639AC7E10F9d54979',
},
],
},
]You can conveniently change the display name of a bank account using the following endpoint. The first argument specifies the ID of the bank account, while the second argument represents the desired new name for the account.
const updateAccount = await client.bankAccount.rename('62d17d3b377dab6c1342136e', 'My new account')You can change the display name of a debit card:
const updatedCard = await client.debitCard.rename(
'62d17d3b377dab6c1342136e',
'My primary debit card'
)You can conveniently change the display name of a bill using the following endpoint. The first argument specifies the ID of the bill, while the second argument represents the desired new name for the account.
const updateAccount = await client.bill.rename('62d17d3b377dab6c1342136e', 'My first credit card')To remove a bank account from a user's account, you can use the following endpoint. You only need to specify the ID of the bank account that you want to delete as an argument.
await client.bankAccount.delete('62d17d3b377dab6c1342136e')To remove a debit card from a user's account:
await client.debitCard.delete('62d17d3b377dab6c1342136e')To remove a bill from a user's account, you can use the following endpoint. You only need to specify the ID of the bill that you want to delete as an argument.
await client.bill.delete('62d17d3b377dab6c1342136e')When adding a new bill for a user, we need to provide a reference to the institution who holds the account for the user. As an example, if a user wanted to add their Chase Visa Credit Card to their Spritz account, the Institution of the account would be Chase Credit Cards and then the account number provided would be the 16-digit card number for their credit card.
Spritz exposes several endpoints to help users find the Institutions of their bill accounts.
const popularInstitutions = await client.institution.popularUSBillInstitutions()
// Optionally filter by a specific bill type
const popularInstitutions = await client.institution.popularUSBillInstitutions(BillType.Mortgage)const institutions = await client.institution.searchUSBillInstitutions('american express')
// Optionally filter by a specific bill type
const institutions = await client.institution.searchUSBillInstitutions(
'american express',
BillType.CreditCard
)A payment request refers to the intent to initiate a payment to a specific account. Once a payment request is created, a blockchain transaction is required to fulfill it. After the blockchain transaction settles, the payment request is completed, and a fiat payment is issued to the designated account.
To initiate a payment request for a specific account, you can use the following functionality. The required inputs for creating a payment request include the ID of the account to be paid, the amount of the fiat payment in USD, and the network on which the blockchain transaction will settle.
When creating a payment request, you can specify how the amount should be calculated using the amountMode parameter:
TOTAL_AMOUNT: The payer covers the entire amount including fees. The specified amount is the total that will be deducted from the payer's wallet.AMOUNT_RECEIVED(default): The payment that arrives in the bank account excludes fees. The specified amount is what the recipient will receive, and fees are added on top.
This allows you to control whether fees are included in or added to the specified amount.
Integrators can subsidize transaction fees on behalf of their users. When enabled, you can cover part or all of the transaction fees, which Spritz will invoice to you separately.
Note: Fee subsidies are a gated feature and must be enabled by the Spritz team before use. Contact Spritz to request access.
Parameters:
feeSubsidyPercentage(string): The percentage of the transaction fee you want to subsidize for the user. For example,"100"covers the entire fee,"50"covers half.maxFeeSubsidyAmount(string, optional): The maximum dollar amount you're willing to subsidize per transaction. If the fee exceeds this cap, the user pays the difference.
How it works:
- Set
feeSubsidyPercentageto define what portion of fees you'll cover - Optionally set
maxFeeSubsidyAmountto cap your exposure - Spritz invoices you separately for the subsidized amounts
- Users see reduced or zero fees on their transactions
Example:
// Cover 100% of fees up to $5 per transaction
const paymentRequest = await client.paymentRequest.create({
amount: 100,
accountId: account.id,
network: PaymentNetwork.Ethereum,
feeSubsidyPercentage: '100',
maxFeeSubsidyAmount: '5',
})
// If transaction fee is $3: integrator pays $3, user pays $0
// If transaction fee is $8: integrator pays $5, user pays $3import {PaymentNetwork, AmountMode} from '@spritz-finance/api-client';
const paymentRequest = await client.paymentRequest.create({
amount: 100,
accountId: account.id,
network: PaymentNetwork.Ethereum,
deliveryMethod: 'INSTANT',
amountMode: AmountMode.TOTAL_AMOUNT // Optional, defaults to AMOUNT_RECEIVED
});
// Example response
{
id: '645399c8c1ac408007b12273',
userId: '63d12d3B577fab6c6382136e',
accountId: '6322445f10d3f4d19c4d72fe',
status: 'CREATED',
amount: 100,
feeAmount: 0,
amountDue: 100,
network: 'ethereum',
createdAt: '2023-05-04T11:40:56.488Z'
}After creating a payment request, you must issue a blockchain transaction to settle the payment request. For EVM compatible networks, this involves interacting with the SpritzPay smart contract (see: SpritzPay deployments).
To obtain the data needed for the transaction, you can use the following endpoint.
import {PaymentNetwork} from '@spritz-finance/api-client';
const paymentRequest = await client.paymentRequest.create({
amount: 100,
accountId: account.id,
network: PaymentNetwork.Ethereum,
});
const transactionData = await client.paymentRequest.getWeb3PaymentParams({
paymentRequest,
paymentTokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', // USDC on mainnet
})
// Example response
{
contractAddress: '0xbF7Abc15f00a8C2d6b13A952c58d12b7c194A8D0',
method: 'payWithToken',
calldata: '0xd71d9632000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000005f5e100000000000000000000000000000000000000000064539a31c1ac408007b12277',
value: null,
requiredTokenInput: '100000000',
}The contract address (to), calldata (data), and value are the primary components used to execute the blockchain transaction. You can use the requiredTokenInput to verify that the user's wallet has sufficient funds to complete the payment before initiating the transaction.
For Solana payments, you need to obtain a versioned transaction that can be signed and submitted to the Solana network.
import {PaymentNetwork} from '@spritz-finance/api-client';
const paymentRequest = await client.paymentRequest.create({
amount: 100,
accountId: account.id,
network: PaymentNetwork.Solana,
});
const transactionData = await client.paymentRequest.getSolanaPaymentParams({
paymentRequest,
paymentTokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC on Solana
signer: 'YourSolanaWalletAddress...',
})
// Example response
{
versionedTransaction: VersionedTransaction, // Deserialized transaction ready to sign
transactionSerialized: 'base64EncodedTransaction...' // Base64 encoded transaction
}The versionedTransaction is a deserialized Solana transaction that can be signed with your wallet and submitted to the network. The transactionSerialized contains the same transaction in base64 encoded format if needed for your implementation.
Transaction fees are applied once the monthly transaction volume exceeds $100. To determine the fee amount for a specific payment value, you can use the following endpoint.
const fees = await client.paymentRequest.transactionPrice(101)
// Example response
0.01Payments represent a fiat payment that has been issued to the account. Once the status of the Payment Request has moved to Confirmed then the Payment will be created.
Payments now include transaction details about the blockchain transaction that fulfilled the payment. When a payment is completed, the transaction field contains:
- hash: The blockchain transaction hash
- from: The wallet address that sent the payment
- asset: The token contract address used for payment
- value: The amount transferred (in the token's smallest unit)
- network: The blockchain network used (e.g., 'ethereum', 'polygon', etc.)
This allows you to track the on-chain transaction that corresponds to each fiat payment.
You can fetch a payment directly by its ID:
const payment = await client.payment.fetchById('6368e3a3ec516e9572bbd23b');
// Example response
{
id: '6368e3a3ec516e9572bbd23b',
userId: '63d12d3B577fab6c6382136e',
status: 'COMPLETED',
accountId: '6322445f10d3f4d19c4d72fe',
amount: 100,
feeAmount: null,
createdAt: '2022-11-07T10:53:23.998Z',
transaction: {
hash: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
from: '0xYourWalletAddress',
asset: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
value: 100000000,
network: 'ethereum'
}
}import {PaymentNetwork} from '@spritz-finance/api-client';
const paymentRequest = await client.paymentRequest.create({
amount: 100,
accountId: account.id,
network: PaymentNetwork.Ethereum,
});
const payment = await client.payment.getForPaymentRequest(paymentRequest.id);
// Example response
{
id: '6368e3a3ec516e9572bbd23b',
userId: '63d12d3B577fab6c6382136e',
status: 'PENDING',
accountId: '6322445f10d3f4d19c4d72fe',
amount: 100,
feeAmount: null,
createdAt: '2022-11-07T10:53:23.998Z',
transaction: null // Will be populated once the payment is fulfilled
}const payments = await client.payment.listForAccount(account.id)
// Example response
[
{
id: '6368e3a3ec516e9572bbd23b',
userId: '63d12d3B577fab6c6382136e',
status: 'COMPLETED',
accountId: '6322445f10d3f4d19c4d72fe',
amount: 100,
feeAmount: null,
createdAt: '2022-11-07T10:53:23.998Z',
transaction: {
__typename: 'BlockchainTransaction',
hash: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
from: '0xYourWalletAddress',
asset: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
value: 100000000,
network: 'ethereum'
}
},
]Onramp Payments are orders to buy crypto stablecoins with a bank transfer. Upon creating an onramp payment, you will receive deposit instructions to fulfill that order. When the bank transfer has been received and disbursed, the status of that onramp payment will change.
const onrampPayment = await client.onrampPayment.create({
token: 'USDC' // Supported: currently only 'USDC'
network: 'ethereum' // supported: 'ethereum', 'polygon', 'avalanche'
amount: 100, // How much token to purchase (100 USDC)
address: '0xbB76483e33e01315438D8F6CF1Aee9C9b85f433b', // Wallet address to disburse tokens to
paymentMethod: 'ACH' // 'WIRE' or 'ACH'
});
// Example response
{
"id": "653fab35ad263e5ae8b0e605",
"amount": 100,
"feeAmount": 1.5,
"depositInstructions": {
"amount": 101.5,
"currency": "USD",
"bankName": "Bank of Nowhere",
"bankAddress": "1800 North Pole St., Orlando, FL 32801",
"bankBeneficiaryName": "Bridge Ventures Inc",
"bankRoutingNumber": "123456789",
"bankAccountNumber": "11223344556677",
"paymentMethod": "WIRE",
"depositMessage": "BVI72D90851F051F4189",
},
"network": "ethereum",
"token": "USDC",
"address": "0xbb76483e33e01315438d8f6cf1aee9c9b85f433b",
"status": "AWAITING_FUNDS",
"createdAt": "2023-10-30T13:10:13.521Z",
}
const payments =
await client.onrampPayment.list()[
// Example response
{
id: '653fab35ad263e5ae8b0e605',
amount: 100,
feeAmount: 1.5,
depositInstructions: {
amount: 101.5,
currency: 'USD',
bankName: 'Bank of Nowhere',
bankAddress: '1800 North Pole St., Orlando, FL 32801',
bankBeneficiaryName: 'Bridge Ventures Inc',
bankRoutingNumber: '123456789',
bankAccountNumber: '11223344556677',
paymentMethod: 'WIRE',
depositMessage: 'BVI72D90851F051F4189',
},
network: 'ethereum',
token: 'USDC',
address: '0xbb76483e33e01315438d8f6cf1aee9c9b85f433b',
status: 'AWAITING_FUNDS',
createdAt: '2023-10-30T13:10:13.521Z',
}
]Instead of making a request to get information, webhooks send information to your specified endpoint as soon as an event occurs. Spritz's integration offers a variety of webhook events that can be crucial for maintaining data integrity and responsiveness in applications. Let's dive into how you can best utilize these.
Spritz currently supports the following webhook events:
account.created: Triggered when a new account is created.account.updated: Triggered when details of an account are updated.account.deleted: Triggered when an account is deleted.
payment.created: Triggered when a new payment is initiated.payment.updated: Triggered when details of a payment are updated.payment.completed: Triggered when a payment is successfully completed.payment.refunded: Triggered when a payment is refunded.
verification.status.updated: Triggered when a user's verification status changes.
These events allow you to respond to changes in the account and payments for a user.
To set up a webhook with Spritz, you'll need to provide:
- URL: The endpoint URL where Spritz will send the webhook data.
- Events: The specific events you want to listen for.
const webhook = await client.webhook.create({
url: 'https://my.webhook.url/spritz',
events: ['account.created', 'account.updated', 'account.deleted'],
})Upon receiving a webhook, your server will get a payload with the following structure:
{
"userId": "user-id-here",
"id": "resource-id-here",
"eventName": "name-of-the-event-here"
}To retrieve all webhooks configured for your integration:
const webhooks = await client.webhook.list()
// Returns an array of webhook configurationsTo delete a specific webhook by its ID:
const deletedWebhook = await client.webhook.delete('webhook-id-here')
// Returns the deleted webhook objectEach webhook request is signed using an HMAC SHA256 signature, based on the exact JSON payload sent in the body. This signature is included in the Signature HTTP header of the request.
The secret key used to compute the signature is the webhook secret you set when configuring your webhook integration. If you have not set a webhook secret, there will be no Signature header in the webhook request.
You can verify webhook authenticity by computing the HMAC signature and comparing it to the Signature header included in the webhook request.
import { createHmac } from "crypto";
const signature = createHmac("sha256", <YOUR_WEBHOOK_SECRET>)
.update(<REQUEST_BODY_AS_JSON_STRING>) // JSON.stringify(payload)
.digest("hex");Ensure that the computed signature matches the Signature header received in the webhook request before processing the payload.
To add or update a webhook secret for signing webhook requests:
const result = await client.webhook.updateWebhookSecret('your-webhook-secret-here')
// Returns: { success: true }This secret will be used to sign all subsequent webhook requests sent to your endpoint. Always store your webhook secret securely and never expose it in client-side code.
The on-ramp feature allows users to purchase cryptocurrency using traditional payment methods (ACH, Wire transfers). Before users can on-ramp, they must complete identity verification and accept terms of service.
Before a user can on-ramp, they must:
- Complete platform-level KYC (identity verification)
- Accept the third-party on-ramp provider's Terms of Service
- Wait for the provider's KYC to process (happens automatically after accepting ToS)
Use the getUserAccess() method to check what features are available to a user and what requirements they need to complete:
const accessCapabilities = await client.user.getUserAccess()
// Check if on-ramp is active
if (accessCapabilities.capabilities.onramp.active) {
console.log('User can on-ramp!')
console.log('Available features:', accessCapabilities.capabilities.onramp.features)
// Features may include: 'ach_purchase', 'wire_purchase'
} else {
console.log('User needs to complete requirements:')
accessCapabilities.capabilities.onramp.requirements.forEach((req) => {
console.log(`- ${req.type}: ${req.description}`)
if (req.actionUrl) {
console.log(` Action URL: ${req.actionUrl}`)
}
})
}const access = await client.user.getUserAccess()
// Check platform-level KYC first
if (!access.kycStatus.verified) {
// User needs to complete platform KYC
if (access.kycRequirement) {
if (access.kycRequirement.actionUrl) {
// Direct user to complete KYC
console.log('Complete KYC at:', access.kycRequirement.actionUrl)
}
if (access.kycRequirement.status === 'failed' && access.kycRequirement.retryable) {
// User can retry failed verification
await client.user.retryFailedVerification()
}
}
}Once platform KYC is complete, the user needs to accept the third-party on-ramp provider's Terms of Service:
const access = await client.user.getUserAccess()
// Find the Terms of Service requirement
const tosRequirement = access.capabilities.onramp.requirements.find(
(req) => req.type === 'terms_acceptance'
)
if (tosRequirement && tosRequirement.actionUrl) {
// Use this URL to guide the customer towards Terms of Service acceptance
// You can embed this URL in an iFrame or display in a new browser window
// For iFrame and WebView implementations:
// Listen to the postMessage event for the signedAgreementId
window.addEventListener('message', (event) => {
if (event.data.signedAgreementId) {
// Use the agreement ID to complete acceptance
await client.onramp.acceptTermsOfService(event.data.signedAgreementId)
}
})
// Direct user to review the terms at tosRequirement.actionUrl
console.log('Terms of Service URL:', tosRequirement.actionUrl)
}After accepting the Terms of Service, the third-party provider's KYC process happens automatically in the background. When you accept the ToS, the platform automatically submits the user's existing KYC data to the provider. The integrator doesn't need to take any action - just monitor the status:
// After accepting ToS, provider KYC is processed automatically
// You can monitor the status but no action is required
const access = await client.user.getUserAccess()
// Check provider KYC status (for monitoring only)
const kycRequirement = access.capabilities.onramp.requirements.find(
(req) => req.type === 'identity_verification'
)
if (kycRequirement) {
switch (kycRequirement.status) {
case 'pending':
console.log('Provider KYC is being processed automatically')
break
case 'failed':
console.log('Provider KYC failed - user may need to retry')
break
default:
// KYC completed successfully if no requirement exists
console.log('Provider KYC complete!')
}
}Use webhooks to be notified when user capabilities change:
// Set up a webhook to monitor capability updates
const webhook = await client.webhook.create({
url: 'https://your-server.com/webhook',
events: ['capabilities.updated'],
})
// In your webhook handler:
// When you receive a 'capabilities.updated' event, check the user's access again
const updatedAccess = await client.user.getUserAccess()
if (updatedAccess.capabilities.onramp.active) {
console.log('User can now on-ramp!')
}Once on-ramp is active, users can create virtual accounts to receive funds:
import { PaymentNetwork, onrampSupportedTokens } from '@spritz-finance/api-client'
// Check supported tokens for a network
const supportedTokens = onrampSupportedTokens[PaymentNetwork.Ethereum]
console.log('Supported tokens on Ethereum:', supportedTokens)
// Output: ['USDC', 'USDT', 'DAI', 'USDP', 'PYUSD']
// Create a virtual account
const virtualAccount = await client.virtualAccounts.create({
network: PaymentNetwork.Ethereum,
address: '0xYourEthereumAddress',
token: 'USDC',
})
// The virtual account includes deposit instructions
if (virtualAccount.depositInstructions) {
const instructions = virtualAccount.depositInstructions
console.log('Bank Name:', instructions.bankName)
console.log('Account Number:', instructions.bankAccountNumber)
console.log('Routing Number:', instructions.bankRoutingNumber)
console.log('Bank Address:', instructions.bankAddress)
// User sends wire/ACH to these details to fund their account
}// Get all virtual accounts for the user
const accounts = await client.virtualAccounts.list()
accounts.forEach((account) => {
console.log(`Network: ${account.network}`)
console.log(`Address: ${account.address}`)
console.log(`Token: ${account.token}`)
console.log(`Deposited: ${account.deposited}`)
if (account.microdeposits) {
console.log('Microdeposits:', account.microdeposits)
}
})The following tokens are supported on each network for virtual accounts:
- Ethereum: USDC, USDT, DAI, USDP, PYUSD
- Polygon: USDC
- Base: USDC
- Arbitrum: USDC
- Avalanche: USDC
- Optimism: USDC
- Solana: USDC, PYUSD
- Tron: USDT
import { SpritzApiClient, Environment, PaymentNetwork } from '@spritz-finance/api-client'
const client = SpritzApiClient.initialize({
environment: Environment.Production,
integrationKey: 'YOUR_INTEGRATION_KEY',
})
// Set user API key
client.setApiKey(userApiKey)
async function setupOnramp() {
// 1. Check current access
const access = await client.user.getUserAccess()
if (!access.capabilities.onramp.active) {
console.log('On-ramp not active. Requirements:')
for (const req of access.capabilities.onramp.requirements) {
if (req.type === 'terms_acceptance' && req.actionUrl) {
// Direct user to accept terms
console.log('Accept terms at:', req.actionUrl)
// After acceptance, call:
// await client.onramp.acceptTermsOfService(agreementId)
}
}
return false
}
// 2. Create virtual account
const virtualAccount = await client.virtualAccounts.create({
network: PaymentNetwork.Ethereum,
address: '0xUserWalletAddress',
token: 'USDC',
})
console.log('Virtual account created!')
console.log('Send funds to:', virtualAccount.depositInstructions)
return true
}
setupOnramp().then((success) => {
if (success) {
console.log('On-ramp setup complete!')
}
})