Complete API documentation for the SwiftRemit smart contract.
All API endpoints are subject to rate limiting to ensure fair usage and system stability. The API implements RFC 6585-compliant rate limiting with multiple tiers.
| Tier | Limit | Window | Applies To |
|---|---|---|---|
| Global | 100 requests | 15 minutes | All unauthenticated requests |
| Per-Key | 200 requests | 1 minute | Authenticated API key requests |
| Admin | 500 requests | 1 minute | Admin operations (x-api-key header) |
| Webhook | 1000 requests | 1 minute | Webhook endpoints |
Every response includes the following RFC 6585-compliant headers:
RateLimit-Limit: Maximum number of requests allowed in the current windowRateLimit-Remaining: Number of requests remaining in the current windowRateLimit-Reset: ISO 8601 timestamp when the rate limit window resets
When the rate limit is exceeded, the API returns:
- HTTP Status:
429 Too Many Requests Retry-Afterheader: Number of seconds to wait before retrying- Response body includes detailed error information
Example 429 Response:
{
"success": false,
"error": {
"message": "Too many requests from this IP, please try again later.",
"code": "RATE_LIMIT_EXCEEDED",
"retryAfter": 120,
"resetAt": "2026-07-30T12:30:00.000Z"
},
"timestamp": "2026-07-30T12:28:00.000Z"
}Response Headers:
HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 2026-07-30T12:30:00.000Z
Retry-After: 120
- Monitor Headers: Check
RateLimit-Remainingbefore making additional requests - Honor Retry-After: When receiving a 429 response, wait at least
Retry-Afterseconds before retrying - Implement Exponential Backoff: For transient errors, use exponential backoff with jitter
- Use API Keys: Authenticate with
x-api-keyheader for higher rate limits (per-key tier) - Cache Responses: Cache responses when appropriate to reduce API calls
- Batch Operations: Use batch endpoints when available to reduce request count
Rate limits are configured via environment variables:
RATE_LIMIT_WINDOW_MS: Global rate limit window in milliseconds (default: 900000 = 15 minutes)RATE_LIMIT_MAX_REQUESTS: Global rate limit max requests (default: 100)API_KEY_RATE_LIMIT_WINDOW_MS: Per-key rate limit window in milliseconds (default: 60000 = 1 minute)API_KEY_RATE_LIMIT_MAX: Per-key rate limit max requests (default: 200)ADMIN_RATE_LIMIT_WINDOW_MS: Admin rate limit window in milliseconds (default: 60000 = 1 minute)ADMIN_RATE_LIMIT_MAX: Admin rate limit max requests (default: 500)WEBHOOK_RATE_LIMIT_WINDOW_MS: Webhook rate limit window in milliseconds (default: 60000 = 1 minute)WEBHOOK_RATE_LIMIT_MAX: Webhook rate limit max requests (default: 1000)
Simulates a settlement to preview fees and payout amount before confirming. No state changes are made.
Request Body:
{ "remittanceId": 1 }Validation:
remittanceIdmust be a positive integer
Response 200:
{
"would_succeed": true,
"payout_amount": "9750",
"fee": "250",
"error_message": null
}Response 400 — invalid input:
{ "error": "remittanceId must be a positive integer" }Response 500 — contract or network error:
{ "error": "Failed to simulate settlement" }Set an admin-managed rolling 24h send limit for a currency/country pair.
Authorization: Admin only
Parameters:
currency: Stringcountry: Stringlimit: i128
Returns: Result<(), ContractError>
Errors:
Unauthorized(20)InvalidAmount(3)
Confirms payout, optionally validating an off-chain commitment proof.
If settlement_config.require_proof is enabled for the remittance, proof must be present and match the stored commitment hash.
Parameters:
remittance_id: u64proof: Option<BytesN<32>>
Additional Errors:
InvalidProof(50)MissingProof(51)
Public view function to inspect request usage in the active rate-limit window.
Returns: (requests_used, max_requests, window_seconds)
Initialize the contract with admin, USDC token, and platform fee.
Authorization: None (can only be called once)
Parameters:
admin: Address- Admin address with full controlusdc_token: Address- USDC token contract addressfee_bps: u32- Platform fee in basis points (0-10000)
Returns: Result<(), ContractError>
Errors:
AlreadyInitialized(1) - Contract already initializedInvalidFeeBps(4) - Fee exceeds 10000 bps (100%)
Example:
soroban contract invoke \
--id $CONTRACT_ID \
--source deployer \
--network testnet \
-- \
initialize \
--admin GXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
--usdc_token CXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX \
--fee_bps 250Register an agent to handle remittances.
Authorization: Admin only
Parameters:
agent: Address- Agent address to register
Returns: Result<(), ContractError>
Errors:
NotInitialized(2) - Contract not initialized
Events: agent_reg(agent)
Example:
soroban contract invoke \
--id $CONTRACT_ID \
--source admin \
--network testnet \
-- \
register_agent \
--agent GXXXXXXXXXXXXXXXXXX
---
## WebSocket — Real-time FX Rate Feed
SwiftRemit exposes a Socket.io namespace at `/fx-rates` (mounted under the WebSocket path `/ws`) for real-time FX rate pushes. Clients subscribe to currency pairs and receive updates within 1 s of each cache refresh.
### Connection
ws://:/ws/fx-rates
Socket.io client path option: `{ path: '/ws' }`
### Events — Client → Server
#### `subscribe`
Subscribe to one or more currency pairs. The server immediately sends the last known rate for each pair (rate-replay).
```json
{ "pairs": ["USD/PHP", "USD/MXN"] }
Unsubscribe from one or more currency pairs.
{ "pairs": ["USD/MXN"] }Emitted whenever the FX cache refreshes a subscribed pair.
{
"pair": "USD/PHP",
"from": "USD",
"to": "PHP",
"rate": 57.83,
"timestamp": "2026-06-28T10:00:01.000Z",
"provider": "ExchangeRateAPI"
}Socket.io handles reconnect automatically. On reconnect the client should re-send subscribe for all pairs it needs; the server will replay the last known rate immediately.
import { io } from 'socket.io-client';
const socket = io('http://localhost:3000', { path: '/ws' }).of('/fx-rates');
socket.on('connect', () => {
socket.emit('subscribe', { pairs: ['USD/PHP', 'USD/MXN'] });
});
socket.on('fx_rate', (update) => {
console.log(`${update.pair}: ${update.rate} @ ${update.timestamp}`);
});