Overview
The Resoon API provides Iranian recipient onboarding and KYC verification services for crypto-to-fiat payouts. PayGear integrates with Resoon to manage user profiles, bank accounts, and verification status.
Base URL: https://resoon.paygear.io/api/v1
Architecture: PayGear calls Resoon's REST API to create/query users. Resoon sends webhook events to PayGear when user status changes occur.
Authentication (HMAC-SHA256)
All v1 API endpoints require HMAC-SHA256 signature authentication to ensure request authenticity and prevent tampering.
Required for: All /api/v1/* endpoints and /api/transaction-request
Required Headers
X-API-Key: Your API key (format: pk_<32-hex-chars>)X-Timestamp: Unix timestamp in milliseconds (must be within 5 minutes)X-Signature: HMAC-SHA256 signature of the requestContent-Type: application/json
Signature Generation
The signature is created by concatenating METHOD + PATH + TIMESTAMP + BODY, then hashing with your API secret:
import crypto from 'crypto';
function generateHMACSignature(
secret: string,
method: string,
path: string,
timestamp: string,
body: string = ''
): string {
const message = `${method.toUpperCase()}${path}${timestamp}${body}`;
return crypto
.createHmac('sha256', secret)
.update(message)
.digest('hex');
}
// Example: POST request
const secret = 'your-64-char-hex-secret';
const timestamp = Date.now().toString();
const method = 'POST';
const path = '/api/v1/users';
const body = JSON.stringify({ person: {...} });
const signature = generateHMACSignature(
secret, method, path, timestamp, body
);
// Add to request headers
headers: {
'X-API-Key': 'pk_c9b5695b8d593f4107d37c04ae3d5d06',
'X-Timestamp': timestamp,
'X-Signature': signature,
'Content-Type': 'application/json'
}Python Example
import hmac
import hashlib
import time
def generate_hmac_signature(
secret: str,
method: str,
path: str,
timestamp: str,
body: str = ""
) -> str:
message = f"{method.upper()}{path}{timestamp}{body}"
return hmac.new(
secret.encode('utf-8'),
message.encode('utf-8'),
hashlib.sha256
).hexdigest()
# Example: GET request
timestamp = str(int(time.time() * 1000))
path = "/api/v1/users/RSN-123/status"
signature = generate_hmac_signature(
"your-secret", "GET", path, timestamp
)Getting API Keys
- Navigate to page
- Click "Generate New Key"
- Add an optional description
- Copy the secret immediately - it's only shown once!
- Store both API key (pk_*) and secret securely
Security: Timestamps must be within 5 minutes of current time. Signatures prevent request tampering and replay attacks.
Common Authentication Errors
| Status | Error | Solution |
|---|---|---|
| 401 | Missing authentication headers | Include X-API-Key, X-Timestamp, X-Signature |
| 401 | Invalid API key format | Key must start with pk_ and be 35 chars |
| 401 | Timestamp too old | Use current timestamp (within 5 minutes) |
| 401 | Invalid signature | Verify method, path, timestamp, and body are correct |
Create User
POST https://resoon.paygear.io/api/v1/usersCreate a new recipient in Resoon's system for KYC verification. Validates Iranian national ID, mobile, IBAN, and bank card details.
Authentication
HMAC-SHA256 Required
See Authentication section above for signature generation details.
Request Body
{
"person": {
"name_en": {
"first_name": "Ali",
"middle_name": "Mohammad",
"last_name": "Mohammadi"
},
"name_fa": {
"first_name": "علی",
"last_name": "محمدی"
},
"date_of_birth": "1991-08-06",
"date_of_birth_shamsi": "1370/05/15",
"codemelli": "0123456789",
"contacts": {
"mobile": "+989121234567",
"email": "ali@example.com"
}
},
"bank_accounts": [
{
"nickname": "حساب اصلی",
"bank_name": "ملی",
"iban": "IR820540102680020817909002",
"card_number": "6037997123456789",
"preferred": true
}
]
}Validation Rules
- codemelli: Must be 10 digits with valid mod-11 checksum
- mobile: Iranian format (+98912...) or local (0912...)
- iban: Must be valid Iranian IBAN (IR + 24 digits, mod-97 checksum)
- card_number: Must be 16 digits
- bank_accounts: At least 1 required
Authentication: HMAC-SHA256 required - See Authentication section above for implementation details
Success Response (201 Created - New User)
{
"resoonId": "RSN-1733512345678-a1b2c3d4",
"status": "pending_verification",
"message": "Profile received. Verification in progress."
}Success Response (200 OK - Existing User with Discrepancies)
{
"resoonId": "RSN-1733512345678-a1b2c3d4",
"status": "pending_verification",
"message": "Existing user found. New information added for verification.",
"is_existing_user": true,
"discrepancies": {
"mobile_mismatch": {
"existing": "+989121234567",
"provided": "+989129999999",
"new_mobile_added": true,
"verification_required": true
},
"bank_accounts_added": [
{
"iban": "IR062960000000100324200002",
"bank_name": "Bank Pasargad",
"verification_required": true
}
]
}
}Duplicate User Handling
When a user with the same codemelli (National ID) already exists:
- Returns existing
resoonIdwith status 200 (not 409 duplicate error) - Mobile mismatch: If mobile differs from existing, adds to
pending_mobilesarray for verification - New bank accounts: Adds any new IBANs with
verification_status: "pending" - Returns detailed
discrepanciesobject showing what was added - Sets
is_existing_user: trueflag
Error Response (422 Validation Error)
{
"error": {
"code": "invalid_format",
"message": "Invalid Iranian IBAN format",
"fields": ["bank_accounts[0].iban"]
}
}Example cURL Request
# First, generate signature (see Authentication section)
TIMESTAMP=$(date +%s000)
BODY='{"person":{"name_en":{"first_name":"Ali","last_name":"Mohammadi"},"date_of_birth":"1990-01-01","date_of_birth_shamsi":"1368/10/11",...}}'
SIGNATURE=$(echo -n "POST/api/v1/users${TIMESTAMP}${BODY}" | openssl dgst -sha256 -hmac "your-secret" | cut -d' ' -f2)
curl -X POST https://resoon.paygear.io/api/v1/users \
-H "Content-Type: application/json" \
-H "X-API-Key: pk_c9b5695b8d593f4107d37c04ae3d5d06" \
-H "X-Timestamp: ${TIMESTAMP}" \
-H "X-Signature: ${SIGNATURE}" \
-d '{"person":{"name_en":{"first_name":"Ali","last_name":"Mohammadi"},"date_of_birth":"1990-01-01","date_of_birth_shamsi":"1368/10/11",...}}'Get User Summary
GET https://resoon.paygear.io/api/v1/users/{resoonId}/summaryRetrieve basic user information with privacy masking. Mobile numbers and IBANs are partially masked to protect user privacy.
Authentication: HMAC-SHA256 required (see Authentication section)
Success Response (200 OK)
{
"resoonId": "RSN-1733512345678-a1b2c3d4",
"full_name": "علی محمدی",
"mobile_masked": "0912***4567",
"status": "verified",
"bank_accounts_preview": [
{
"accountId": "acc-123",
"nickname": "حساب اصلی",
"bank_name": "ملی",
"iban_last4": "9002",
"preferred": true
}
],
"created_at": "2024-12-06T10:30:00Z"
}Privacy Notes
- Mobile: Shows first 4 and last 4 digits (0912***4567)
- IBAN: Only last 4 digits shown
- Card numbers: Never exposed via API
- Full codemelli: Never exposed via API
Get User Status
GET https://resoon.paygear.io/api/v1/users/{resoonId}/statusCheck current verification lifecycle status.
Authentication: HMAC-SHA256 required (see Authentication section)
Possible Statuses
draft- Initial creation, not submittedscreening- Under automated screeningpending_verification- Awaiting manual reviewverified- KYC approved, ready for payoutsrejected- KYC failed
Success Response (200 OK)
{
"resoonId": "RSN-1733512345678-a1b2c3d4",
"status": "verified",
"status_reasons": []
}Register Webhook
POST https://resoon.paygear.io/api/v1/webhook-registrationRegister PayGear's webhook URL to receive event notifications when user status changes, bank accounts are added, etc.
Authentication: HMAC-SHA256 required (see Authentication section)
Request Body
{
"webhook_url": "https://api.paygear.com/webhooks/resoon",
"webhook_secret": "your_webhook_secret_minimum_32_chars_long",
"events": [
"user.status.changed",
"user.bank_account.added",
"user.bank_account.preferred.changed"
]
}Validation
- webhook_url must use HTTPS protocol
- webhook_secret should be strong (recommended 32+ characters)
- events array is optional (defaults to all events)
Success Response (200 OK)
{
"message": "Webhook registered successfully",
"events": [
"user.status.changed",
"user.bank_account.added",
"user.bank_account.preferred.changed"
],
"registered_at": "2024-12-06T10:30:00Z"
}Webhooks (Resoon → PayGear)
Resoon sends HMAC-signed webhook events to PayGear when lifecycle changes occur. PayGear must implement a webhook receiver endpoint and verify signatures.
Webhook Structure
POST https://api.paygear.com/webhooks/resoon
Content-Type: application/json
X-Resoon-Signature: sha256=<hmac_hex_digest>
{
"event": {
"event_id": "evt-status-1733512345678-a1b2c3d4",
"event_type": "user.status.changed",
"resoonId": "RSN-1733512345678-a1b2c3d4",
"old_status": "pending_verification",
"new_status": "verified",
"occurred_at": "2024-12-06T10:30:00Z"
}
}Signature Verification (Node.js)
CRITICAL: PayGear MUST verify the X-Resoon-Signature header to ensure webhook authenticity and prevent spoofing attacks.
import { createHmac } from 'crypto';
function verifyResoonWebhook(
body: string,
signature: string,
secret: string
): boolean {
const expectedSignature = 'sha256=' +
createHmac('sha256', secret)
.update(body)
.digest('hex');
return signature === expectedSignature;
}
// Usage in Express.js webhook handler
app.post('/webhooks/resoon',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.headers['x-resoon-signature'];
const body = req.body.toString('utf8');
if (!verifyResoonWebhook(body, signature, process.env.RESOON_WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const payload = JSON.parse(body);
handleResoonEvent(payload.event);
res.json({ received: true });
});Event Types
user.status.changed- User verification status changed (pending → verified, etc.)user.bank_account.added- User added a new payout bank accountuser.bank_account.preferred.changed- User changed their preferred payout account
Retry Logic
Resoon implements exponential backoff retry:
- Retries: 3 attempts
- Backoff: 1s → 2s → 4s (max 5s)
- Timeout: 10 seconds per attempt
Best Practice: PayGear should respond with 200 OK quickly (< 5 seconds), process events asynchronously, and return { "received": true } immediately.
Important Notes
Authentication: All v1 endpoints require HMAC-SHA256 signature authentication. Get your API keys from the page.
Privacy: Resoon returns masked data to protect user privacy. Full IBANs, card numbers, and codemelli are never exposed via API.
Webhook Security: Always verify the X-Resoon-Signature header in webhook requests. Store your webhook secret securely.
Data Validation: Resoon validates Iranian national IDs (codemelli), mobile numbers, IBANs, and bank cards using proper checksum algorithms.
Error Codes
| Code | Status | Description |
|---|---|---|
invalid_format | 422 | Validation failed |
user_not_found | 404 | resoonId doesn't exist |
duplicate_candidate | 200 | User already exists (returns existing ID with discrepancies) |
unauthorized | 401 | Auth required |
rate_limited | 429 | Too many requests |
internal_error | 500 | Server error |
Rate Limiting
Rate limits protect the API from abuse:
- General: 100 requests/minute per IP
- User Creation: 10 requests/minute per IP
- Webhook Registration: 5 requests/hour
Support & Resources
For questions or issues with the API:
- View complete API reference documentation
- Check for system status
- Contact the PayGear development team