Quote
**Quote ** powers business flows such as deposits, withdrawals and currency exchanges.
Quotes lock in rates and fees for a limited time and drive both funding instructions and payout execution.
High-level concepts
- Quote: a priced offer describing source/target currencies, amounts, fees, and validity window.
- Funding method: how the quote is funded (wallet, bank/Interac, card, crypto, etc.).
- Quote type:
EXCHANGE,DEPOSIT, orWITHDRAWAL. - Status lifecycle:
CREATED→ quote generated, not yet accepted.AWAITING_PAYMENT/AWAITING_DEPOSIT→ quote accepted, waiting for funds.PAYMENT_CONFIRMED/DEPOSIT_CONFIRMED→ funds secured.PROCESSING_PAYOUT→ payout leg in progress.COMPLETED,EXPIRED, orFAILED→ terminal states.
Quote flows
The quote system supports advanced business use‑cases with payment tracking and crypto funding:
- Adds dedicated payment tracking fields to the
quotestable:payment_method,payment_currency,payment_network,payment_address,payment_sender_name.payment_source_amount,payment_source_to_usd_rate,payment_usd_to_target_rate,payment_target_amount.
- Supports crypto funding via stablecoins (e.g. USDC/USDT) by bridging through USD for accurate fee and limit calculations.
Crypto quote behaviour
For crypto‑funded quotes, the system:
- Validates
payment_method == CRYPTOand ensures the requested token/network are supported. - Maps display currencies (what the business sees) to internal quote currencies and payment currencies, depending on quote type:
- DEPOSIT: user funds with stablecoin → receives fiat; fees calculated on USD equivalent.
- WITHDRAWAL: user holds fiat → receives stablecoin; fiat side limits enforced.
- DIRECT_EXCHANGE: stablecoin → fiat via USD bridge.
- Uses USD as an intermediate currency to:
- Compute exchange rates.
- Calculate and convert fees correctly between stablecoin and fiat.
- Derive the final total payable amount in the asset the user actually transfers.
How /business integrates with quotes
Business clients typically interact with quotes as part of a broader /business integration:
-
Authentication
- All quote‑related calls from business systems must be signed using the HMAC scheme described in the Authentication section, even when the underlying path is not prefixed with
/businessinternally.
- All quote‑related calls from business systems must be signed using the HMAC scheme described in the Authentication section, even when the underlying path is not prefixed with
-
Webhook notifications
- When a quote leads to a completed transaction and the owner is a business user with a configured webhook URL, the Business Webhook System sends a unified webhook payload with:
transaction_typematching the quote type (DEPOSIT/WITHDRAWAL/EXCHANGE).statusreflecting the final transaction state.referenceandtransaction_idfor reconciliation.
- When a quote leads to a completed transaction and the owner is a business user with a configured webhook URL, the Business Webhook System sends a unified webhook payload with:
-
Reconciliation & reporting
- Business systems can combine webhook events with quote responses to:
- Reconcile fees and total payable amounts.
- Track conversion and completion rates for quotes.
- Monitor limits and declined requests (e.g. 422 responses for limit violations).
- Business systems can combine webhook events with quote responses to:
/business endpoint from the OpenAPI spec
From docs/openapi.yaml, the following /business route is explicitly defined and is typically used before initiating quotes or payouts for a new contact:
Onboard a business contact using Footprint
- Method:
POST - Path:
/business/contact/onboard - Summary: Onboard a business contact using a Footprint KYC token.
Request body (from the OpenAPI spec):
{
"token_id": "fp_token_xyz"
}token_id(string, required): Footprint token ID for the KYC session.
Successful response (200) – User schema (from components.schemas.User):
{
"id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
"email": "business.contact@example.com",
"first_name": "Jane",
"middle_name": "A.",
"last_name": "Doe",
"phone_number": "+2348012345678",
"tag": "business-contact",
"role": "CUSTOMER",
"user_type": "BUSINESS",
"user_status": "ACTIVE",
"kyc_status": "APPROVED",
"title": "Ms",
"date_of_birth": "1990-01-01T00:00:00Z",
"avatar_url": "https://cdn.transfaar.com/avatars/jane.png",
"is_email_verified": true,
"is_totp_2fa_activated": false,
"country_code": "CA",
"address_line_1": "123 Example Street",
"address_line_2": "Suite 4B",
"postal_code": "A1B2C3",
"city": "Toronto",
"province": "ON",
"created_at": "2025-01-30T12:20:15Z",
"updated_at": "2025-01-30T12:20:15Z"
}Error responses (from the OpenAPI spec):
400– Token is required (Errorschema).403– Unmatched email for user record against Footprint email (Errorschema).500– Internal server error (Errorschema).
Create Quote
Create a new quote to lock in exchange rates and fees for a currency conversion or money transfer. Quotes are valid for a limited time (typically 30 minutes) and must be accepted before expiration.
Endpoint
POST /api/v1/quotes/v2
Authentication
This endpoint requires JWT authentication. Include your access token in the Authorization header:
Authorization: Bearer <your-access-token>Request Body
{
"source_currency": "USD",
"target_currency": "NGN",
"source_amount": "1000.00",
"beneficiary_id": "123e4567-e89b-12d3-a456-426614174000",
"fee_config_id": "2fbdb973-39fd-4217-aa9b-e7b8ccc9e457",
"quote_type": "EXCHANGE",
"payment_method": "FIAT",
"tz": "UTC",
"narration": "Salary payment for John Doe"
}Required Parameters
| Parameter | Type | Description |
|---|---|---|
source_currency | string | Source currency code (ISO 4217, e.g., "USD", "EUR") |
target_currency | string | Target currency code (ISO 4217, e.g., "NGN", "GBP") |
source_amount | string | Amount in source currency (decimal string, e.g., "1000.00") |
fee_config_id | string (UUID) | Fee configuration ID for the selected payment rail |
quote_type | string | Transaction type: EXCHANGE, WITHDRAWAL, DEPOSIT, SWAP, DIRECT_EXCHANGE, or TRANSFER |
payment_method | string | Funding method: FIAT or CRYPTO |
tz | string | Request timezone (IANA format, e.g., "UTC", "America/New_York") |
Optional Parameters
| Parameter | Type | Description |
|---|---|---|
beneficiary_id | string (UUID) | Beneficiary ID. Required for EXCHANGE, DIRECT_EXCHANGE, and FIAT WITHDRAWAL; optional for DEPOSIT, SWAP, TRANSFER, and CRYPTO WITHDRAWAL |
narration | string | Optional description for the quote |
expected_source_interac_email | string (email) | Required for INTERAC transactions (non-WITHDRAWAL) |
payment_network | string | Crypto network when payment_method=CRYPTO (e.g., "ETH", "POLYGON") |
payment_address | string | Wallet address for crypto payments when payment_method=CRYPTO |
user_tag | string | User tag for TRANSFER transactions |
origin_reference | string | Origin reference for external client APIs |
Quote Types
EXCHANGE- Convert one currency to anotherWITHDRAWAL- Withdraw funds to a beneficiaryDEPOSIT- Deposit funds into your accountSWAP- Swap between currenciesDIRECT_EXCHANGE- Direct currency exchangeTRANSFER- Transfer funds between users (V2 feature)
Success Response (201 Created)
{
"quote_id": "123e4567-e89b-12d3-a456-426614174000",
"source": {
"currency": "USD",
"amount": "1000.00"
},
"target": {
"currency": "NGN",
"amount": "1500000.00"
},
"exchange_rate": "1500.00",
"narration": "Salary payment for John Doe",
"fees": {
"processing_fee": "10.00",
"deposit_fee": "5.00",
"payout_fee": "2.50",
"total_fees": "17.50",
"fee_currency": "USD"
},
"funding_method": {
"type": "BANKACCOUNT",
"provider_id": 1,
"provider_name": "Payaza",
"provider_reference": "PAY-REF-12345"
},
"total_payable": "1017.50",
"valid_until": "2025-03-09T01:40:53Z",
"quote_status": "CREATED",
"type": "EXCHANGE"
}Response Fields
| Field | Type | Description |
|---|---|---|
quote_id | string (UUID) | Unique identifier for the quote |
source | object | Source currency and amount |
target | object | Target currency and amount |
exchange_rate | string | Exchange rate used for conversion |
fees | object | Breakdown of all fees (processing, deposit, payout, total) |
funding_method | object | Payment method details (type, provider, reference) |
total_payable | string | Total amount including all fees |
valid_until | string (ISO 8601) | Quote expiration timestamp |
quote_status | string | Current status (typically "CREATED") |
type | string | Quote type |
narration | string | Description provided in request |
Error Responses
| Status Code | Description |
|---|---|
400 | Invalid request format or missing required fields |
401 | Unauthorized - Invalid or missing JWT token |
404 | Fee configuration or related resource not found |
422 | Limit bounds or business rule violation (e.g., amount exceeds limits) |
500 | Internal server error |
Example: cURL
curl -X POST /api/v1/quotes/v2 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{
"source_currency": "USD",
"target_currency": "NGN",
"source_amount": "1000.00",
"beneficiary_id": "123e4567-e89b-12d3-a456-426614174000",
"fee_config_id": "2fbdb973-39fd-4217-aa9b-e7b8ccc9e457",
"quote_type": "EXCHANGE",
"payment_method": "FIAT",
"tz": "UTC",
"narration": "Salary payment"
}'Example: HTTP Request
POST /api/v1/quotes/v2 HTTP/1.1
Host: api.sznd.app
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"source_currency": "USD",
"target_currency": "NGN",
"source_amount": "1000.00",
"beneficiary_id": "123e4567-e89b-12d3-a456-426614174000",
"fee_config_id": "2fbdb973-39fd-4217-aa9b-e7b8ccc9e457",
"quote_type": "EXCHANGE",
"payment_method": "FIAT",
"tz": "UTC",
"narration": "Salary payment"
}Example: Crypto-Funded Quote
For crypto-funded quotes, include the payment network and address:
{
"source_currency": "USDC",
"target_currency": "NGN",
"source_amount": "1000.00",
"fee_config_id": "2fbdb973-39fd-4217-aa9b-e7b8ccc9e457",
"quote_type": "DEPOSIT",
"payment_method": "CRYPTO",
"payment_network": "ETH",
"payment_address": "0x1234abcd5678ef901234abcd5678ef90abcd1234",
"tz": "UTC"
}Get Quote By ID
Retrieve detailed information about a specific quote, including exchange rates, fees, status, and validity period.
Endpoint
GET /api/v1/quotes/{id}
Authentication
This endpoint requires JWT authentication. Include your access token in the Authorization header:
Authorization: Bearer <your-access-token>Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | The unique identifier of the quote to retrieve |
Success Response (200 OK)
{
"quote_id": "123e4567-e89b-12d3-a456-426614174000",
"source": {
"currency": "USD",
"amount": "1000.00"
},
"target": {
"currency": "NGN",
"amount": "1500000.00"
},
"exchange_rate": "1500.00",
"narration": "Salary payment for John Doe",
"fees": {
"processing_fee": "10.00",
"deposit_fee": "5.00",
"payout_fee": "2.50",
"total_fees": "17.50",
"fee_currency": "USD"
},
"funding_method": {
"type": "BANKACCOUNT",
"provider_id": 1,
"provider_name": "Payaza",
"provider_reference": "PAY-REF-12345"
},
"total_payable": "1017.50",
"valid_until": "2025-03-09T01:40:53Z",
"quote_status": "CREATED",
"type": "EXCHANGE"
}Response Fields
The response structure is identical to the Create Quote response. See the Create Quote section for field descriptions.
Error Responses
| Status Code | Description |
|---|---|
400 | Invalid quote ID format |
401 | Unauthorized - Invalid or missing JWT token |
403 | Forbidden - User does not own this quote |
404 | Quote not found |
500 | Internal server error |
Example: cURL
curl -X GET /api/v1/quotes/123e4567-e89b-12d3-a456-426614174000 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."Example: HTTP Request
GET /api/v1/quotes/123e4567-e89b-12d3-a456-426614174000 HTTP/1.1
Host: api.sznd.app
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/jsonUse Cases
- Check Quote Status - Verify if a quote is still valid before accepting it
- Display Quote Details - Show users the exchange rate, fees, and total payable amount
- Reconciliation - Retrieve quote details for transaction reconciliation
- Audit Trail - Access historical quote information for reporting
Important Notes
- Quote Ownership - You can only retrieve quotes that belong to your authenticated account
- Quote Expiration - Quotes expire after a set period (typically 30 minutes). Check the
valid_untilfield before accepting - Status Changes - Quote status may change after creation (e.g., from
CREATEDtoACCEPTEDorEXPIRED) - Rate Locking - Exchange rates are locked when the quote is created and remain valid until expiration
Next Steps
After creating a quote, you'll typically want to:
- Accept the Quote - Use the quote ID to accept and proceed with the transaction
- Monitor Status - Poll the quote status or set up webhooks to track quote lifecycle
- Handle Expiration - Create a new quote if the current one expires before acceptance
For more information, see:
- Accept Quote - How to accept a quote and initiate the transaction
- Webhooks - Set up webhooks to receive real-time quote status updates