Error Codes
Complete reference of error responses returned by the surstrom API.
HTTP Status Codes
| Code | Meaning |
|---|
200 | Success |
204 | Silent success (SDK endpoints) |
400 | Bad request — validation error or client misconfiguration |
401 | Unauthorized — missing or invalid authentication |
403 | Forbidden — wrong role or account status |
429 | Rate limited — too many requests |
500 | Internal server error |
502 | Provider error — upstream provider failure |
503 | Service unavailable — no providers in pool |
Authentication Errors
| Error | Status | Description |
|---|
API key required | 401 | No x-api-key header provided |
Invalid API key format | 401 | Key is not a valid UUID |
API key not found | 401 | No matching key in database |
API key owner must be CLIENT | 403 | Only CLIENT accounts can use API keys |
Account not active | 403 | Account is suspended or rejected |
Unauthorized | 401 | No valid session cookie |
Forbidden | 403 | Wrong role for the endpoint |
General Request Errors
| Error | Status | Description |
|---|
Invalid JSON body | 400 | Request body is not valid JSON |
Internal server error | 500 | Unexpected server error |
Payment Validation Errors
Returned as { "error": "Validation failed", "details": [...] } with status 400.
| Field | Error | Description |
|---|
amount | required | Amount not provided |
amount | must be between 2 and 1000 | Out of valid range |
amount | max 2 decimal places | Too many decimal places |
maxFee | required | maxFee not provided |
maxFee | must be a non-negative number | Invalid fee value |
maxFee | max 2 decimal places | Too many decimal places |
currency | required | Currency not provided |
currency | must be usd | Only USD supported currently |
externalId | required | External ID not provided |
externalId | max 36 characters | Too long |
externalId | only Latin letters, digits, - and _ allowed | Invalid characters |
suffixClient | must be exactly 4 characters | Wrong length |
suffixClient | Latin letters only | Non-Latin characters |
suffixClient | blocked value | Reserved suffix (test, demo, fake, tmp, temp, dev, debug, mock, stub, foo, bar, baz, xxx, zzz, aaa, asdf, qwer) |
theme | must be light or dark | Invalid theme value |
Payment Routing Errors
| Error | Status | Description |
|---|
No providers available | 503 | No matching provider found |
Client withdraw address not configured | 400 | Client hasn't set withdraw address |
maxFee too low to cover referral fees | 400 | Referral fees exceed maxFee |
Payment creation failed | 502 | All provider attempts failed |
Referrer wallet/fee count mismatch | 400 | Referral configuration error |
No Provider Reasons
When the error is No providers available, the reason field explains:
| Reason | Description |
|---|
no_providers_in_pool | No providers are currently in the pool |
currency_mismatch | No providers support the requested currency |
fee_too_low | Client's maxFee is lower than any provider's fee |
amount_out_of_range | Payment amount is outside all providers' min/max range |
insufficient_relay_balance | Matching providers don't have enough balance |
rejected_by_all_relays | All matching providers have rejected this client |
unknown | Unspecified matching failure |
Wallet Errors
| Error | Status | Description |
|---|
Minimum withdrawal is 2 USDC | 400 | Amount below minimum |
Max 3 decimal places allowed | 400 | Too many decimal places |
2FA code required | 400 | No TOTP code provided |
2FA required | 400 | 2FA not enabled on account |
Invalid 2FA code | 400 | Wrong TOTP code |
No wallet found | 400 | User has no wallet |
Withdraw address not set | 400 | No withdraw address configured |
Invalid Solana address | 400 | Address is not a valid Solana pubkey |
Cannot withdraw to your own deposit address | 400 | Withdraw address same as deposit |
Active providers in pool | 400 | Must remove providers from pool first |
Insufficient available balance | 400 | Not enough balance after pending |
Balance sync error - contact support | 400 | Negative available balance detected |
Lookup Errors
| Error | Status | Description |
|---|
API key required | 401 | No x-api-key header provided |
Invalid API key | 401 | Key not found or invalid |
Payment not found | 404 | No matching payment for this user |
Webhook Errors
| Error | Status | Description |
|---|
Only CLIENT users can use webhooks | 403 | Relay tried to set webhook |
URL is required | 400 | No URL provided |
URL must be HTTP or HTTPS | 400 | Invalid protocol |
Internal URLs are not allowed | 400 | Localhost or private IP |
Invalid URL format | 400 | Unparseable URL |
Webhook test failed | 400 | Server didn't return 200 |
Provider Errors
| Error | Status | Description |
|---|
Missing required fields | 400 | Incomplete connection request |
Invalid signature | 400 | Tampered account data |
This account is already connected by another relay | 400 | Duplicate provider account |
Invalid publishable key format | 400 | Wrong key prefix |
Publishable key is required | 400 | No publishable key provided |
Provider not found | 404 | Provider doesn't exist or not owned |
Provider Settings Errors
| Error | Status | Description |
|---|
Invalid webhook URL | 400 | Unparseable webhook URL |
Webhook URL must be HTTP or HTTPS | 400 | Wrong protocol |
Statement suffix must be uppercase... | 400 | Only A-Z, 0-9, spaces, dots, hyphens, underscores, and template syntax allowed |
Payment description contains invalid characters... | 400 | Invalid characters in description |
Metadata must be an object | 400 | Wrong metadata type |
Metadata can have at most 10 keys | 400 | Too many metadata keys |
Metadata keys must be strings of max 40 characters | 400 | Key too long |
Metadata keys must be alphanumeric with underscores only | 400 | Invalid key format |
Metadata values must be strings of max 500 characters | 400 | Value too long |
Invalid minimum amount | 400 | Min amount not a valid number |
Invalid maximum amount | 400 | Max amount not a valid number |
Invalid daily limit | 400 | Daily limit not a valid number |
Minimum/Maximum amount can have at most 2 decimal places | 400 | Too many decimals |
Daily limit must be a whole number | 400 | Decimals not allowed |
Fee must be between 0 and 100 | 400 | Fee out of range |
Fee can have at most 2 decimal places | 400 | Too many decimals |
Minimum cannot exceed maximum | 400 | Invalid range |
Provider Delete Errors
| Error | Status | Description |
|---|
in_pool | 400 | Must remove from pool before deleting |
active_payments | 400 | Has pending payments (includes count) |
SDK Verification Errors
| Error | Status | Description |
|---|
URL required | 400 | No embed URL provided |
Invalid URL format | 400 | Unparseable URL |
Session expired | 401 | Session is invalid |
Verification failed | 500 | Internal verification error |
Rate Limit Response
All rate-limited requests return:
Rate limit responsejson
{
"error": "Too many requests",
"retryAfter": 30
}
With headers:
Retry-After: 30 — Seconds until rate limit resets
X-RateLimit-Remaining: 0 — Remaining requests in window
Rate Limit Presets
| Endpoint | Limit |
|---|
| Payment init | 10/second per API key |
| Auth operations | 10/minute per IP |
| Provider operations | 30/minute per user |
| Sensitive operations | 5/minute per user |
| SDK verification | 10/minute per user |
| Balance/transactions | 30/minute per user |
| Withdrawal | 2/minute per user |
| Pool toggle | 3/minute per user |
| TOTP operations | 5/minute per user |
| General API | 100/minute per user |
Rate limiting is in-memory per server instance. In a multi-instance deployment, actual limits may be higher than configured values.