Authentication
Authentication
Every request to /api/v1/merchant is authenticated with a secret API key, sent as a Bearer token in the Authorization header.
Secret API keys
Keys come in two environments, distinguished by their prefix. Use the test key against Base Sepolia while you integrate, and the live key once you go to mainnet.
| Prefix | Environment | Settles on |
|---|---|---|
rsend_test_… | Test | Base Sepolia testnet (test tokens, no real value) |
rsend_live_… | Live | Mainnet — enabled per jurisdiction as RSends rolls out |
curl https://pay.rsends.io/api/backend/api/v1/merchant/transactions \
-H "Authorization: Bearer rsend_test_YOUR_KEY"How keys are issued
Keys are self-serve. Sign in, open /app → API keys → Merchant keys and create one. You need the admin role in your organisation. Every key minted this way is rsend_test_…, scoped write, and bound to the test environment; live keys are issued as mainnet is enabled for your jurisdiction. You can hold up to 5 active keys per environment — a sixth returns 409 max_keys_reached. Revoking is immediate and idempotent.
Shown once, hashed at rest
One prerequisite: your company profile
Before you can mint a key you have to submit your organisation's company profile, in /app. It is a single self-serve form — nobody has to approve you to use the sandbox. Submit it and testnet access is immediate.
| Field | Notes |
|---|---|
legal_name | Registered company name |
country | Country of registration |
registration_number | Company registration number |
tax_or_vat_number | Tax or VAT number |
business_category | What the business does |
expected_monthly_volume | Rough band, not a commitment |
countries_served | Where your customers are |
primary_stablecoin | The token you expect to settle in |
| declarations | Two must be affirmed (no sanctioned countries, no prohibited activities) plus a PEP disclosure. trading_name and website are optional. |
Until it is submitted, every /api/v1/merchant/* call returns 403 — with a valid key, too. That is the first thing to check when a fresh integration answers 403 to everything.
{ "detail": { "code": "company_profile_required" } }
// If your organisation was explicitly declined:
{ "detail": { "code": "approval_declined", "reason": "…" } }Going live is where a human looks
rsend_live_ key requires an approved organisation, and the same call with a live key returns 403 approval_pending until that happens. The sandbox deliberately does not wait on anyone — it settles faucet tokens on a testnet.Scopes & environments
Each key is bound to a single environment (test or live) and a scope. Enforcement is by HTTP method: any non-GET request made with a read key is rejected with 403 INSUFFICIENT_SCOPE. A read-only key can pull reporting data but cannot create intents.
| Scope | Grants |
|---|---|
read | GET only — payment intents, transactions and reporting |
write | Everything read does, plus create/cancel/resolve intents and manage webhooks. This is what self-serve keys get |
admin | Everything write does. Not self-provisionable |
The environment binding is enforced on both reads and writes: a test key creating an intent on a mainnet chain gets 400 TESTNET_ONLY, and a live key on a testnet chain gets 400 MAINNET_ONLY. Test and live data never mix — that includes webhook deliveries.
Retrying safely
Send an X-Idempotency-Key header on any POST and a retry with the same key replays the original response instead of re-running the operation. The record is kept for 24 hours, and it is scoped to your account and environment — no other merchant's key can collide with yours. Resend the original body unchanged: the same key with a different body is 409 IDEMPOTENCY_KEY_REUSED. A retry that arrives while the first request is still in flight gets 409 DUPLICATE_REQUEST_IN_FLIGHT. See Errors.
Authentication errors
A missing, malformed, or revoked key returns 401 with INVALID_API_KEY. See Errors for the full table.
/api/v1/merchant/transactions