Payment Widget
Everything you need to know to add crypto on/off-ramp payments to your website.
What Is SeevCash?
SeevCash lets your customers buy and sell USDC (a US dollar-pegged cryptocurrency on the Stellar blockchain) using Mobile Money — directly from your website.
- Buy USDC — Customer pays with MTN MoMo, Vodafone Cash, or AirtelTigo Money → receives USDC in their Stellar wallet
- Sell USDC — Customer sends USDC → receives GHS in their Mobile Money wallet
You don't need to handle any blockchain logic. We take care of everything.
How It Works (User Experience)
Sell USDC Flow (Withdraw → Mobile Money Payout):
- Merchant backend initializes a widget session (
POST /api/v1/widget/session) - Merchant backend authenticates with the anchor via SEP-10
- Merchant backend requests a SEP-24 withdraw interactive URL with merchant session fields
- Customer is redirected to the interactive URL in an iframe/popup
- Customer completes KYC and enters Mobile Money payout details
- Customer sends USDC to the anchor's Stellar address with the provided memo
- SeevCash receives USDC → disburses GHS to customer's MoMo (1–5 minutes)
- Merchant receives webhook notification on completion
Quick Start (5 Minutes)
Step 1: Register Your Merchant Account
curl --location 'https://widget-backend.seevcash.com/api/v1/merchant/register' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Acme Payments",
"legal_name": "Acme Payments Ltd.",
"email": "payments@acmepay.com",
"password": "MerchantPass123!"
}'Response (201):
{
"data": {
"id": "a227bfda-6a91-4749-a360-bbbd2eeb8eff",
"name": "Acme Payments",
"legal_name": "Acme Payments Ltd.",
"email": "payments@acmepay.com",
"status": "pending",
"tier": "starter",
"fee_base_bps": 100,
"fee_markup_bps": 0,
"monthly_volume_limit": 100000,
"current_month_volume": 0,
"stellar_public_key": "GCGZIEFPQL3L3FDXCQKEY273QIVBNTWNANH6RBHXASVJ7SE423VTIUZL",
"is_test_mode": false,
"created_at": "2026-05-13T12:30:07.592877236Z",
"updated_at": "2026-05-13T12:30:07.592877236Z"
},
"meta": {
"request_id": "ffce4778-f125-4b5c-9ca7-42edfae871cb",
"timestamp": "2026-05-13T12:30:07.720850177Z"
}
}Key fields:
| Field | Meaning |
|---|---|
id | Your unique merchant ID |
status: "pending" | Becomes active after KYB verification |
tier: "starter" | Determines your volume limits |
fee_base_bps: 100 | Your base fee (100 bps = 1%) |
stellar_public_key | Auto-generated, used for blockchain auth behind the scenes |
Step 2: Log In
curl --location 'https://widget-backend.seevcash.com/api/v1/merchant/login' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "payments@acmepay.com",
"password": "MerchantPass123!"
}'Response (200):
{
"data": {
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmMjVlM2U1Zi1lODNhLTRlYmEtOTNiOS01MDQ0ZDc2NDU2NzMiLCJleHAiOjE3Nzg3NTQxNzQsImlhdCI6MTc3ODY2Nzc3NH0..."
},
"meta": {
"request_id": "7e5b2ee9-f130-44e0-8487-c9135633fa23",
"timestamp": "2026-05-13T10:22:54.108683834Z"
}
}The
access_tokenis a JWT valid for 24 hours. Use it asAuthorization: Bearer <token>for all merchant dashboard endpoints.
Step 3: Create an API Key
curl --location 'https://widget-backend.seevcash.com/api/v1/merchant/apikeys' \
--header 'Authorization: Bearer <your_access_token>' \
--header 'Content-Type: application/json' \
--data '{
"key_type": "public",
"domains": ["http://localhost:3000", "https://myapp.example.com"]
}'Response (201):
{
"data": {
"api_key": {
"id": "e16c5b6e-2c6e-47ee-98c9-7646f0eaae2f",
"merchant_id": "f25e3e5f-e83a-4eba-93b9-5044d7645673",
"key_type": "public",
"key_prefix": "sv_public_07",
"allowed_domains": [
"http://localhost:3000",
"https://myapp.example.com"
],
"allowed_ips": [],
"permissions": ["onramp", "offramp"],
"rate_limit_rpm": 600,
"is_active": true,
"created_at": "2026-05-13T12:32:30.232150975Z"
},
"key": "sv_public_07386d7477d35f4243fc6b56a5d09aa58a244a742514b270d7ed2a646ca12f15"
},
"meta": {
"request_id": "ad1947d6-1a0d-447f-9b22-b376c5d76beb",
"timestamp": "2026-05-13T12:32:30.241750377Z"
}
}⚠️ The
keyfield is only shown once. Store it securely.
Key types:
| Type | Use For | Security |
|---|---|---|
public | Frontend widget script (data-api-key) | Domain-restricted |
secret | Server-to-server API calls | IP-restricted recommended |
API Reference
Base URL: https://widget-backend.seevcash.com
All responses are wrapped in { "data": {...}, "meta": {...} } format.
Initialize Widget Session
Creates a session for widget API calls. The widget does this automatically, but you can also call it server-side.
⚠️ The
user_idmust be a verified SEP-12 customer ID obtained from either the Widget Customer API or the Anchor SEP-12 flow. See Customer KYC below.
POST /api/v1/widget/session
X-API-Key: <your_api_key>
Content-Type: application/json{
"user_id": "6a3ce6751dbd5c0082a8d1b5"
}Response (201):
{
"data": {
"session_id": "2e72ff62-9d3e-4e54-8378-fa3c9e85bd0e",
"session_token": "0c28199e-cd8b-4ea5-8c3b-980c778944e5",
"merchant_id": "f25e3e5f-e83a-4eba-93b9-5044d7645673",
"expires_at": "2026-05-13T11:54:53.289405068Z"
}
}Sessions expire after 30 minutes. If the customer's KYC is not
ACCEPTED, you'll receive a403 KYC_NOT_APPROVEDerror.
Customer KYC (Verification)
Before a customer can transact (buy/sell USDC), they must pass KYC verification. SeevCash supports two flows:
| Flow | Best For | Who Handles KYC |
|---|---|---|
| Widget Customer API | Merchants who want a simple REST API | Widget handles everything |
| Anchor SEP-12 Direct | Developers familiar with Stellar anchor protocols | You interact with the anchor directly |
Both flows produce a customer ID that you use as the user_id in all subsequent API calls.
Option A: Widget Customer API (Recommended)
The simplest way to onboard customers. You send customer details to the widget API, and it handles SEP-12 registration with the anchor behind the scenes.
Step 1: Create a Customer
curl --location 'https://widget-backend.seevcash.com/api/v1/widget/customer' \
--header 'X-API-Key: sv_secret_<your_secret_key>' \
--header 'Content-Type: application/json' \
--data-raw '{
"first_name": "Patrick",
"last_name": "Oduro",
"email_address": "patrick@example.com",
"birth_date": "1990-05-15",
"address": "123 Main Street",
"city": "Accra",
"address_country_code": "GHA",
"mobile_number": "+233244123456"
}'Response (201):
{
"data": {
"id": "6a3ce6751dbd5c0082a8d1b5",
"status": "ACCEPTED"
},
"meta": {
"request_id": "ddc93e15-410e-4f68-bc99-43953f951a98",
"timestamp": "2026-06-25T08:27:33.622249018Z"
}
}| Field | Description |
|---|---|
id | The customer ID — use this as user_id in all subsequent calls |
status | ACCEPTED = ready to transact, NEEDS_INFO = more fields required, PROCESSING = under review |
Required fields:
| Field | Type | Description |
|---|---|---|
first_name | string | Customer's given name |
last_name | string | Customer's family name |
email_address | string | Valid email address |
Optional fields (improve approval speed):
| Field | Type | Example |
|---|---|---|
birth_date | string | "1990-05-15" (YYYY-MM-DD) |
address | string | "123 Main Street" |
city | string | "Accra" |
state_or_province | string | "Greater Accra" |
postal_code | string | "00233" |
address_country_code | string | "GHA" (ISO 3166-1 alpha-3) |
mobile_number | string | "+233244123456" (with country code) |
Step 2: Check Customer Status
If the status is PROCESSING or NEEDS_INFO, poll this endpoint:
curl --location 'https://widget-backend.seevcash.com/api/v1/widget/customer/6a3ce6751dbd5c0082a8d1b5' \
--header 'X-API-Key: sv_secret_<your_secret_key>'Response (200):
{
"data": {
"id": "6a3ce6751dbd5c0082a8d1b5",
"status": "ACCEPTED",
"provided_fields": {
"first_name": { "type": "string", "description": "Given or first name", "status": "ACCEPTED" },
"last_name": { "type": "string", "description": "Family or last name", "status": "ACCEPTED" },
"email_address": { "type": "string", "description": "Email address", "status": "ACCEPTED" },
"birth_date": { "type": "date", "description": "Date of birth (YYYY-MM-DD)", "status": "ACCEPTED" }
}
}
}If status is NEEDS_INFO, the response includes a fields object listing what's still required:
{
"data": {
"id": "6a3ce6751dbd5c0082a8d1b5",
"status": "NEEDS_INFO",
"fields": {
"email_address": { "type": "string", "description": "Email address" },
"last_name": { "type": "string", "description": "Family or last name" }
}
}
}To provide the missing fields, call POST /api/v1/widget/customer again with the same data plus the missing fields — the customer record will be updated (idempotent).
Step 3: Delete a Customer
If a customer requests data deletion or you need to remove them:
curl --location --request DELETE 'https://widget-backend.seevcash.com/api/v1/widget/customer/6a3ce6751dbd5c0082a8d1b5' \
--header 'X-API-Key: sv_secret_<your_secret_key>'Response (200):
{
"data": { "deleted": true }
}This removes the customer from both the SeevCash anchor and the widget database. The customer ID can no longer be used.
Step 4: Use the Customer ID
Once your customer has status: "ACCEPTED", use their ID as user_id everywhere:
# Initialize session
curl -X POST 'https://widget-backend.seevcash.com/api/v1/widget/session' \
--header 'X-API-Key: sv_secret_<your_key>' \
--header 'Content-Type: application/json' \
--data '{ "user_id": "6a3ce6751dbd5c0082a8d1b5" }'
# Create orders, interactive flows, etc. — all use the same customer IDOption B: Anchor SEP-12 Direct Flow
For developers who want direct control over the Stellar anchor's KYC protocol. You interact with the anchor at https://anchor.seevcash.com using SEP-10 authentication and SEP-12 customer management.
Prerequisites
- A Stellar keypair (your merchant's keypair from registration, or any funded Stellar account)
- Understanding of SEP-10 (authentication) and SEP-12 (KYC)
Step 1: SEP-10 Authentication
Get a challenge transaction and sign it with your Stellar secret key:
# Get challenge
curl 'https://anchor.seevcash.com/auth?account=GABC...YOUR_STELLAR_PUBLIC_KEY'
# Response: { "transaction": "<XDR_CHALLENGE>", "network_passphrase": "..." }
# Sign the challenge with your secret key and submit:
curl -X POST 'https://anchor.seevcash.com/auth' \
--header 'Content-Type: application/json' \
--data '{ "transaction": "<SIGNED_XDR>" }'
# Response: { "token": "eyJhbG..." }The token is valid for 15 minutes.
Step 2: Create Customer (PUT /customer)
curl --location --request PUT 'https://anchor.seevcash.com/customer' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <SEP-10_JWT>' \
--data-raw '{
"first_name": "Patrick",
"last_name": "Oduro",
"email_address": "patrick@example.com",
"birth_date": "1990-05-15",
"address": "123 Main Street",
"city": "Accra",
"address_country_code": "GHA",
"mobile_number": "+233244123456"
}'Response (202):
{
"id": "6a3ce6751dbd5c0082a8d1b5"
}Per SEP-12,
PUT /customeronly returns the customer ID. Query status withGET /customer.
Step 3: Check Status (GET /customer)
curl 'https://anchor.seevcash.com/customer?id=6a3ce6751dbd5c0082a8d1b5' \
--header 'Authorization: Bearer <SEP-10_JWT>'Response (200):
{
"id": "6a3ce6751dbd5c0082a8d1b5",
"status": "ACCEPTED",
"provided_fields": {
"first_name": { "type": "string", "description": "Given or first name", "status": "ACCEPTED" },
"last_name": { "type": "string", "description": "Family or last name", "status": "ACCEPTED" },
"email_address": { "type": "string", "description": "Email address", "status": "ACCEPTED" }
}
}Step 4: Use the Customer ID in Widget
The customer ID from the anchor can be used directly in the widget APIs:
curl -X POST 'https://widget-backend.seevcash.com/api/v1/widget/session' \
--header 'X-API-Key: sv_secret_<your_key>' \
--header 'Content-Type: application/json' \
--data '{ "user_id": "6a3ce6751dbd5c0082a8d1b5" }'SEP-12 Customer Statuses
| Status | Meaning | Action |
|---|---|---|
ACCEPTED | KYC approved — customer can transact | ✅ Proceed |
PROCESSING | Under review by compliance | ⏳ Poll periodically |
NEEDS_INFO | Missing required information | Submit additional fields via PUT /customer |
REJECTED | KYC permanently denied | ❌ Customer cannot transact |
SEP-12 Field Statuses (per field)
| Status | Meaning |
|---|---|
ACCEPTED | Field verified and approved |
PROCESSING | Field under review |
REJECTED | Field rejected (see error for reason) |
VERIFICATION_REQUIRED | Field needs verification (e.g., OTP for phone) |
Delete Customer (SEP-12)
curl --location --request DELETE 'https://anchor.seevcash.com/customer/6a3ce6751dbd5c0082a8d1b5' \
--header 'Authorization: Bearer <SEP-10_JWT>'Response (200):
{ "message": "customer deleted" }Comparing the Two Flows
| Widget Customer API | Anchor SEP-12 Direct | |
|---|---|---|
| Auth | API Key (X-API-Key) | SEP-10 JWT (Stellar keypair signing) |
| Complexity | Simple REST calls | Requires Stellar SDK & key management |
| Endpoint | widget-backend.seevcash.com | anchor.seevcash.com |
| Customer ID | Same format, interchangeable | Same format, interchangeable |
| Best for | Web/mobile apps, fintech integrations | Stellar wallets, DeFi protocols |
| KYC handled by | Widget (automatic) | You (manual SEP-10 → SEP-12 flow) |
💡 Both flows produce the same customer ID format. A customer created via Option A can be queried via Option B and vice versa.
Anchor Integration (Direct SEP-24 Flow)
For merchants who want programmatic control over the payment flow — building custom UIs, triggering withdrawals from their backend, or integrating into existing apps — you can interact directly with the SeevCash Stellar Anchor using the Stellar Wallet SDK.
Deposit & Withdraw (Interactive URLs)
After completing Customer KYC, you can initiate deposit (buy USDC) and withdraw (sell USDC) flows. There are two approaches:
| Approach | Auth | Best For |
|---|---|---|
| Widget API (recommended) | API Key (X-API-Key) | Simple server-to-server integration |
| Anchor SEP-24 Direct | SEP-10 JWT + session fields | Stellar-native wallets, full protocol control |
Both return an interactive URL that you open in an iframe or popup for the customer to complete the payment.
Option A: Widget Interactive API (Recommended)
The widget handles SEP-10 authentication, session management, and anchor communication. You just need your API keys and a verified customer ID.
Prerequisites
| Credential | How to Get | Example |
|---|---|---|
sv_secret_ key | POST /merchant/apikeys with key_type: "secret" | sv_secret_4a95a7cc... |
sv_public_ key | POST /merchant/apikeys with key_type: "public" | sv_public_61f8ac8b... |
| Customer ID | POST /widget/customer (must be ACCEPTED) | 6a3cec4b21ea9c49b5d34534 |
⚠️ The secret key goes in the
X-API-Keyheader (server-to-server). The public key goes in the request bodyapi_keyfield (used to generate the widget token).
Withdraw (Sell USDC → Receive Mobile Money)
Customer sends USDC to the anchor, receives GHS in their Mobile Money wallet.
curl --location 'https://widget-backend.seevcash.com/api/v1/widget/withdraw/interactive' \
--header 'X-API-Key: sv_secret_4a95a7cc50961ec5f0b6329c2b5aefb767a68ee3cf5b5133344a18549cad1d42' \
--header 'Content-Type: application/json' \
--data '{
"asset_code": "USDC",
"user_id": "6a3cec4b21ea9c49b5d34534",
"api_key": "sv_public_61f8ac8b75e8a55a16cdea49d2eff4392cf557b9c22df24ecca0eb07de2cdaba",
"onchain_amount": 1
}'Request fields:
| Field | Type | Required | Description |
|---|---|---|---|
asset_code | string | ✅ | Always "USDC" |
user_id | string | ✅ | SEP-12 customer ID (must be ACCEPTED) |
api_key | string | ✅ | Your public API key (sv_public_...) |
onchain_amount | number | Optional | Amount of USDC to sell. If omitted, customer enters in the interactive UI |
Response (200):
{
"data": {
"url": "https://exchange.seevcash.com/interactive?token=eyJhbG...",
"id": "05c6ce07-18c8-48f8-b7b1-e5d66b735d3d",
"type": "interactive_customer_info_needed"
},
"meta": {
"request_id": "d3fb956f-62d9-4e62-8b59-4c26e48674f6",
"timestamp": "2026-06-25T09:26:16.375369413Z"
}
}Deposit (Buy USDC with Mobile Money)
Customer pays with Mobile Money, receives USDC in their Stellar wallet.
curl --location 'https://widget-backend.seevcash.com/api/v1/widget/deposit/interactive' \
--header 'X-API-Key: sv_secret_4a95a7cc50961ec5f0b6329c2b5aefb767a68ee3cf5b5133344a18549cad1d42' \
--header 'Content-Type: application/json' \
--data '{
"asset_code": "USDC",
"user_id": "6a3cec4b21ea9c49b5d34534",
"api_key": "sv_public_f69d93fb31adcb64d9f85d823be4236048e9bdc37bca4d5950182aa6d9b619fb",
"fiat_amount": 13,
"destination_address": "GCGES3SODXJX6RYF2ATRQIA5GLWDXTEXR6QWX3JERVCDY4EIFEMFB55O"
}'Request fields:
| Field | Type | Required | Description |
|---|---|---|---|
asset_code | string | ✅ | Always "USDC" |
user_id | string | ✅ | SEP-12 customer ID (must be ACCEPTED) |
api_key | string | ✅ | Your public API key (sv_public_...) |
fiat_amount | number | Optional | Amount in GHS to spend. If omitted, customer enters in the interactive UI |
destination_address | string | Optional | Stellar address to receive USDC. If omitted, customer enters in the UI |
Response (200):
{
"data": {
"url": "https://exchange.seevcash.com/interactive?token=eyJhbG...",
"id": "fa80ebf4-b431-42ce-a255-e3680e221e09",
"type": "interactive_customer_info_needed"
},
"meta": {
"request_id": "09193866-b809-4834-a9d8-682bf44be9ac",
"timestamp": "2026-06-25T09:27:03.127272637Z"
}
}Response Fields
| Field | Description |
|---|---|
url | Interactive URL — open in iframe, popup, or redirect the customer here |
id | Transaction ID — use to track status via GET /transaction?id=... |
type | Always "interactive_customer_info_needed" |
What Happens After Getting the URL
- Open the
urlin an iframe, popup, or full-page redirect - Customer selects their Mobile Money provider (MTN, Vodafone, AirtelTigo)
- Customer enters their phone number and confirms payment
- For deposits: Customer approves the MoMo prompt → receives USDC (1–4 min)
- For withdrawals: Customer sees anchor's Stellar address + memo → sends USDC → receives GHS (1–5 min)
- You receive a webhook when the transaction completes
Option B: Anchor SEP-24 Direct Flow
For developers building Stellar-native wallets or who need full protocol control. You interact directly with the anchor at https://anchor.seevcash.com.
💡 We recommend using the Stellar Wallet SDK for SEP-24 integration. It handles SEP-10 authentication, token management, and transaction tracking automatically.
Prerequisites
- A Stellar keypair (your merchant's keypair from registration)
- A widget session (
POST /api/v1/widget/session) - A verified customer ID (SEP-12
ACCEPTED)
Install the Stellar Wallet SDK
npm install @stellar/typescript-wallet-sdk @stellar/stellar-sdkComplete Flow with SDK (Recommended)
import {
Wallet,
SigningKeypair,
StellarConfiguration,
} from '@stellar/typescript-wallet-sdk';
const ANCHOR_HOME_DOMAIN = 'anchor.seevcash.com';
async function initiateWithdraw(
signerSecret: string, // Your merchant's Stellar secret key
merchantId: string, // From merchant registration
sessionId: string, // From POST /widget/session
sessionToken: string, // From POST /widget/session
apiKey: string, // Your public key (sv_public_...)
userId: string, // SEP-12 customer ID (ACCEPTED)
) {
const accountKp = SigningKeypair.fromSecret(signerSecret);
const wallet = new Wallet({
stellarConfiguration: StellarConfiguration.MainNet(),
});
const anchor = wallet.anchor({ homeDomain: ANCHOR_HOME_DOMAIN });
// Step 1: SEP-10 Authentication (SDK handles challenge signing)
const sep10 = await anchor.sep10();
const authToken = await sep10.authenticate({ accountKp });
// Step 2: SEP-24 Withdraw
const sep24 = await anchor.sep24();
const { url, id, type } = await sep24.withdraw({
assetCode: 'USDC',
authToken,
extraFields: {
merchant_id: merchantId,
session_id: sessionId,
session_token: sessionToken,
api_key: apiKey,
user_id: userId,
},
});
// url → open in iframe/popup for customer
// id → transaction ID for tracking
return { url, id, type };
}
async function initiateDeposit(
signerSecret: string,
merchantId: string,
sessionId: string,
sessionToken: string,
apiKey: string,
userId: string,
stellarAccount: string, // Customer's Stellar address to receive USDC
) {
const accountKp = SigningKeypair.fromSecret(signerSecret);
const wallet = new Wallet({
stellarConfiguration: StellarConfiguration.MainNet(),
});
const anchor = wallet.anchor({ homeDomain: ANCHOR_HOME_DOMAIN });
const sep10 = await anchor.sep10();
const authToken = await sep10.authenticate({ accountKp });
const sep24 = await anchor.sep24();
const { url, id, type } = await sep24.deposit({
assetCode: 'USDC',
authToken,
destinationAccount: stellarAccount,
extraFields: {
merchant_id: merchantId,
session_id: sessionId,
session_token: sessionToken,
api_key: apiKey,
user_id: userId,
},
});
return { url, id, type };
}Track Transaction Status with SDK
async function checkTransaction(signerSecret: string, transactionId: string) {
const accountKp = SigningKeypair.fromSecret(signerSecret);
const wallet = new Wallet({ stellarConfiguration: StellarConfiguration.MainNet() });
const anchor = wallet.anchor({ homeDomain: ANCHOR_HOME_DOMAIN });
const sep10 = await anchor.sep10();
const authToken = await sep10.authenticate({ accountKp });
const sep24 = await anchor.sep24();
const tx = await sep24.getTransactionBy({ authToken, id: transactionId });
console.log(tx.status); // "completed", "pending_user_transfer_start", etc.
return tx;
}Required extraFields
All five fields below are required for a production integration and must be sent on every deposit and withdraw request.
| Field | Type | Required | Description |
|---|---|---|---|
merchant_id | string | ✅ Required | Your merchant ID from registration |
session_id | string | ✅ Required | From POST /widget/session response |
session_token | string | ✅ Required | From POST /widget/session response |
api_key | string | ✅ Required | Your public API key (sv_public_...) |
user_id | string | ✅ Required | SEP-12 customer ID (must be ACCEPTED) |
⚠️ Omitting any of these returns
400 Bad Request(e.g.api_key is required) and no transaction is created. While experimenting against the testnet host they may be accepted when absent, but do not rely on that — build and test with all five present so your integration behaves identically in production.
Getting Session Credentials
Before calling the anchor, create a widget session:
curl -X POST 'https://widget-backend.seevcash.com/api/v1/widget/session' \
--header 'X-API-Key: sv_secret_<your_key>' \
--header 'Content-Type: application/json' \
--data '{ "user_id": "6a3cec4b21ea9c49b5d34534" }'
# Response:
# { "data": { "session_id": "...", "session_token": "...", "merchant_id": "...", "expires_at": "..." } }Advanced: Raw HTTP Calls (without SDK)
If you can't use the SDK, here are the raw endpoints. You'll need to handle SEP-10 authentication manually.
SEP-10 Authentication
# 1. Get challenge
curl 'https://anchor.seevcash.com/auth?account=<YOUR_STELLAR_PUBLIC_KEY>'
# Response: { "transaction": "<XDR_CHALLENGE>", "network_passphrase": "..." }
# 2. Sign the XDR with your secret key and submit
curl -X POST 'https://anchor.seevcash.com/auth' \
--header 'Content-Type: application/json' \
--data '{ "transaction": "<SIGNED_XDR>" }'
# Response: { "token": "eyJhbG..." } (valid 15 minutes)Deposit Interactive
curl --location 'https://anchor.seevcash.com/transactions/deposit/interactive' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <SEP-10_JWT>' \
--data-raw '{
"asset_code": "USDC",
"account": "GCGES3SODXJX6RYF2ATRQIA5GLWDXTEXR6QWX3JERVCDY4EIFEMFB55O",
"amount": "13",
"merchant_id": "d92e377f-2456-4186-b6a5-cbce930a6219",
"session_id": "5900c14c-a19f-49df-abbe-666cef7d5593",
"session_token": "d345545a-951d-4ebe-b7e3-d7050a120436",
"api_key": "sv_public_f69d93fb31adcb64d9f85d823be4236048e9bdc37bca4d5950182aa6d9b619fb",
"user_id": "6a3cec4b21ea9c49b5d34534"
}'Withdraw Interactive
curl --location 'https://anchor.seevcash.com/transactions/withdraw/interactive' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <SEP-10_JWT>' \
--data-raw '{
"asset_code": "USDC",
"account": "GA2E5D24D2LCGVA3SVVKDP64OTN2M2NLUWRDYINB352HW35ABZ66MPVR",
"amount": "1",
"merchant_id": "d92e377f-2456-4186-b6a5-cbce930a6219",
"session_id": "e70d3556-4d6b-47fe-8d33-bacd17c9cab1",
"session_token": "cc7fad85-b9ce-48de-bdfb-89d3d87ab9fe",
"api_key": "sv_public_61f8ac8b75e8a55a16cdea49d2eff4392cf557b9c22df24ecca0eb07de2cdaba",
"user_id": "6a3cec4b21ea9c49b5d34534"
}'Request Fields (Both Endpoints)
| Field | Type | Required | Description |
|---|---|---|---|
asset_code | string | ✅ Required | "USDC" |
merchant_id | string | ✅ Required | Your merchant ID |
session_id | string | ✅ Required | From POST /widget/session |
session_token | string | ✅ Required | From POST /widget/session |
api_key | string | ✅ Required | Your public API key (sv_public_...) |
user_id | string | ✅ Required | SEP-12 customer ID (must be ACCEPTED) |
account | string | Optional | Stellar public key. Defaults to JWT's account |
amount | string | Optional | Amount (fiat for deposit, USDC for withdraw) |
quote_id | string | Optional | From SEP-38 quote (for guaranteed rates) |
lang | string | Optional | Language code (default: "en") |
Response
{
"type": "interactive_customer_info_needed",
"url": "https://exchange.seevcash.com/interactive?token=eyJhbG...",
"id": "05c6ce07-18c8-48f8-b7b1-e5d66b735d3d"
}Track Transaction
curl 'https://anchor.seevcash.com/transaction?id=<TRANSACTION_ID>' \
--header 'Authorization: Bearer <SEP-10_JWT>'Transaction Status Flow
Deposit: incomplete → pending_user_transfer_start → pending_anchor → pending_stellar → completed
Withdraw: incomplete → pending_user_transfer_start → pending_external → pending_payout_confirmation → completed| Status | Meaning | What's Happening |
|---|---|---|
incomplete | Interactive UI not yet completed | Customer hasn't finished the form |
pending_user_transfer_start | Waiting for customer action | Deposit: awaiting MoMo approval. Withdraw: awaiting USDC transfer |
pending_anchor | Anchor processing | Fiat received, preparing crypto delivery |
pending_stellar | Blockchain transaction submitted | USDC sent, awaiting confirmation |
pending_external | Crypto received by anchor | USDC received, initiating fiat payout |
pending_payout_confirmation | Fiat payout in progress | Mobile Money disbursement initiated |
completed | Done ✅ | Transaction finished successfully |
expired | Timed out | Customer didn't complete within window |
error | Failed | See message field for details |
Network Configuration
| Environment | Widget Backend | Anchor Domain | Network |
|---|---|---|---|
| Production | widget-backend.seevcash.com | anchor.seevcash.com | Stellar Mainnet |
| Testnet | widget-backend.seevcash.com | anchor.seevcash.com | Stellar Testnet |
Comparing Widget vs Anchor Direct for Interactive Flows
| Widget API | Anchor SEP-24 (SDK) | |
|---|---|---|
| Endpoint | POST /widget/deposit/interactive | SDK: sep24.deposit() / sep24.withdraw() |
| Auth | X-API-Key: sv_secret_... | SEP-10 (SDK handles automatically) |
| Session mgmt | Automatic | Manual (create session, pass fields) |
| Language | Any (REST API) | TypeScript/JavaScript |
| When to use | Server-to-server, any language | Stellar wallet apps, JS/TS projects |
| Same interactive URL? | ✅ Yes | ✅ Yes |
| Same transaction ID? | ✅ Yes | ✅ Yes |
💡 Both approaches generate the same interactive URL pointing to
exchange.seevcash.com. The customer experience is identical regardless of which method you use.
Get Widget Config
GET /api/v1/widget/config
X-API-Key: <your_api_key>Response (200):
{
"data": {
"merchant_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"theme": { "primary_color": "#4F46E5", "border_radius": "8px" },
"allowed_coins": ["USDC"],
"redirect_url": "https://mystore.com/payment-complete"
}
}Update Widget Config
PUT /api/v1/merchant/widget-config
Authorization: Bearer <access_token>
Content-Type: application/json{
"theme": { "primary_color": "#10B981", "border_radius": "12px" },
"allowed_coins": ["USDC"],
"redirect_url": "https://mystore.com/thank-you"
}List Merchant Orders
GET /api/v1/merchant/orders?limit=20&cursor=
Authorization: Bearer <access_token>Response (200):
{
"data": [
{
"id": "ord_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "completed",
"fiat_currency": "GHS",
"fiat_amount": 100.0,
"crypto_currency": "USDC",
"crypto_amount": 6.45,
"payment_method": "mobile_money",
"created_at": "2026-05-13T10:30:00Z"
}
],
"next_cursor": "ord_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"has_more": false
}Other Merchant Dashboard Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/v1/merchant/profile | GET | View your merchant profile |
/api/v1/merchant/apikeys/:id | DELETE | Revoke an API key |
/api/v1/merchant/kyb/submit | POST | Submit KYB (Know Your Business) documents |
/api/v1/merchant/kyb/status | GET | Check KYB verification status |
All require Authorization: Bearer <access_token>.
Required Headers
| Header | Used In | Description |
|---|---|---|
X-API-Key | Widget endpoints | Your merchant API key |
X-Session-Id | Session-protected endpoints | From /widget/session response |
X-Session-Token | Session-protected endpoints | From /widget/session response |
X-Idempotency-Key | Order creation | Unique UUID to prevent duplicates |
Authorization | Dashboard endpoints | Bearer <access_token> from login |
Content-Type | All POST/PUT | application/json |
Error Responses
All errors follow this format:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "fiat_amount must be greater than 0"
}
}| Code | HTTP Status | Meaning |
|---|---|---|
VALIDATION_ERROR | 400 | Request body failed validation |
MISSING_HEADER | 400 | Required header missing (e.g., X-Idempotency-Key) |
UNAUTHORIZED | 401 | Invalid or expired API key / session / token |
FORBIDDEN | 403 | Domain not in allowed list |
NOT_FOUND | 404 | Order or resource not found |
DUPLICATE_ORDER | 409 | Idempotency key already used |
PHONE_NOT_VERIFIED | 422 | Phone number hasn't been OTP-verified |
QUOTE_EXPIRED | 422 | Quote expired, request a new one |
RATE_LIMITED | 429 | Too many requests |
INTERNAL_ERROR | 500 | Something went wrong on our end |
Transaction Statuses
| Status | Meaning |
|---|---|
pending_user_transfer_start | Waiting for customer to complete payment |
pending_anchor | Fiat received, crypto delivery in progress |
pending_stellar | Crypto sent, awaiting blockchain confirmation |
pending_external | Crypto received, fiat payout initiated |
pending_payout_confirmation | Fiat payout in progress |
completed | Transaction finished successfully |
failed | Something went wrong |
expired | Customer didn't complete within the time window |
Webhooks
Configure a webhook URL in your merchant dashboard for server-side notifications.
Payload Example:
{
"event": "order.completed",
"order_id": "ord_abc123",
"type": "onramp",
"fiat_amount": "100.00",
"fiat_currency": "GHS",
"crypto_amount": "6.45",
"crypto_currency": "USDC",
"destination_address": "GABCD...XYZ",
"timestamp": "2026-05-13T10:30:00Z"
}Security Headers:
| Header | Description |
|---|---|
X-Webhook-Signature | HMAC-SHA256 signature of the payload body |
X-Webhook-Timestamp | Unix timestamp when sent |
Verify using your webhook secret (found in dashboard).
Events:
| Event | Description |
|---|---|
order.completed | Crypto delivered (onramp) or fiat paid out (offramp) |
order.failed | Payment failed or declined |
order.expired | Payment window timed out |
Retry Policy:
If your endpoint returns non-2xx, we retry:
- Immediate → 2. 10 seconds → 3. 1 minute → 4. 1 hour → 5. 4 hours
After 5 failed attempts, delivery is marked exhausted.
Supported Payment Methods
| Method | Buy | Sell | Speed |
|---|---|---|---|
| MTN Mobile Money | ✅ | ✅ | 1–4 min |
| Vodafone Cash | ✅ | ✅ | 1–4 min |
| AirtelTigo Money | ✅ | ✅ | 1–4 min |
| Bank Transfer | 🚧 | 🚧 | Coming Soon |
Supported Assets
| Asset | Network | Description |
|---|---|---|
| USDC | Stellar | US Dollar-pegged stablecoin (1 USDC ≈ $1) |
Fees
| Fee Type | Description |
|---|---|
| Platform fee | Percentage of transaction (your fee_base_bps + fee_markup_bps) |
| Network fee | Stellar transaction fee (fractions of a cent) |
| Exchange spread | Built into the quoted rate |
All fees are shown to the customer before they confirm.
Security
- All data in transit uses TLS/HTTPS
- The widget handles all sensitive payment details — nothing touches your servers
- Customer phone numbers are OTP-verified before any payment
- Idempotency keys prevent duplicate charges
- API keys can be scoped to specific domains and IP addresses
- Webhooks are HMAC-SHA256 signed for verification
API Key Best Practices
- Use
publickeys for frontend widget,secretkeys for server-to-server - Restrict keys to your allowed domains and IPs
- Never expose
secretkeys in client-side code - Use separate keys for development and production
- Rotate keys periodically
Testing
Use test mode during development:
- Stellar testnet (no real money moves)
- Mobile Money prompts are simulated
- All flows work identically to production
Switch to your live API key when ready to go live.
FAQ
Q: Do I need to understand blockchain? A: No. Add the script tag, receive webhooks. We handle everything else.
Q: What if a payment fails? A: No crypto is sent, no money deducted. Customer can retry.
Q: How fast are transactions? A: 1–4 minutes for buying, 1–5 minutes for selling.
Q: Can I customize the widget? A: Yes — theme colors, allowed coins, and redirect URLs via the widget config API.
Q: Minimum/maximum amounts? A: Configurable per merchant during onboarding.
Q: What if a customer doesn't have a Stellar wallet? A: They need one to receive USDC. Recommend Lobstr or StellarX.
Support
- Email: merchants@seevcash.com
- Dashboard: dashboard.seevcash.com
- Docs: docs.seevcash.com
Quick Start Checklist
- Register merchant account
- Log in and create API key (store it securely!)
- Create a customer via
/widget/customer(KYC verification) - Confirm customer status is
ACCEPTED - Initialize widget session with the customer ID as
user_id - Test a buy and sell flow
- Configure webhook endpoint
- Verify webhook signatures
- Switch to live API key
- Go live 🚀