Error Codes Reference
This page provides a comprehensive reference of all error codes you may encounter when using the Transfer API.
Error Response Format
All errors follow this structure:
{
"error": "error_code",
"error_description": "Human-readable description of the error",
"error_uri": "https://docs.paysera.com/errors/error_code"
}
| Field | Type | Always Present | Description |
|---|---|---|---|
error | string | ✅ Yes | Machine-readable error code |
error_description | string | Usually | Human-readable error message |
error_uri | string | Sometimes | Link to error documentation |
HTTP Status Codes
| Status | Category | Meaning |
|---|---|---|
200 | Success | Request completed successfully |
400 | Client Error | Bad request or invalid parameters |
401 | Client Error | Authentication failed |
403 | Client Error | Access forbidden |
404 | Client Error | Resource not found |
406 | Client Error | Not acceptable format |
409 | Client Error | Resource state conflict |
500 | Server Error | Internal server error |
Standard Error Codes
These error codes are common across all API endpoints:
invalid_request
HTTP Status: 400 Bad Request
When it occurs:
- Request body is malformed
- Invalid JSON syntax
- Wrong Content-Type header
- Missing required headers
Example:
{
"error": "invalid_request",
"error_description": "Request body is not valid JSON"
}
How to fix:
- Validate JSON syntax before sending
- Ensure Content-Type is
application/json - Check all required headers are present
- Verify request structure matches API specification
Code Example:
// ✅ Correct
$data = json_encode($transferData);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception('Invalid JSON: ' . json_last_error_msg());
}
$headers = [
'Content-Type: application/json',
'Authorization: MAC id="..."'
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
invalid_parameters
HTTP Status: 400 Bad Request
When it occurs:
- Required parameter is missing
- Parameter has wrong data type
- Parameter value is invalid
- Field validation failed
Example:
{
"error": "invalid_parameters",
"error_description": "Parameter 'amount' is required",
"errors": {
"amount": "This field is required",
"currency": "Must be a valid 3-letter currency code"
}
}
Common parameter errors:
| Parameter | Error | Solution |
|---|---|---|
amount | Missing or invalid | Provide positive decimal number |
currency | Invalid code | Use 3-letter ISO code (EUR, USD, etc.) |
account_number | Invalid format | Check IBAN format |
callback.url | Invalid URL | Use valid HTTPS URL |
How to fix:
- Check all required fields are provided
- Validate data types before sending
- Ensure values meet format requirements
- Review field validation rules
Code Example:
function validateTransferData($data) {
$errors = [];
// Required fields
if (empty($data['amount'])) {
$errors['amount'] = 'Amount is required';
} elseif (!is_numeric($data['amount']) || $data['amount'] <= 0) {
$errors['amount'] = 'Amount must be a positive number';
}
if (empty($data['currency'])) {
$errors['currency'] = 'Currency is required';
} elseif (!preg_match('/^[A-Z]{3}$/', $data['currency'])) {
$errors['currency'] = 'Currency must be 3 letters (e.g., EUR)';
}
if (!empty($errors)) {
throw new ValidationException('Validation failed', $errors);
}
return true;
}
unauthorized
HTTP Status: 401 Unauthorized
When it occurs:
- MAC signature is invalid
- Client ID not recognized
- Timestamp too old or in future
- Nonce already used
- Missing Authorization header
Example:
{
"error": "unauthorized",
"error_description": "MAC signature verification failed"
}
Common causes:
| Issue | Cause | Solution |
|---|---|---|
| Invalid signature | Wrong MAC calculation | Verify MAC algorithm and key |
| Wrong client_id | Typo or wrong credentials | Check client ID from Paysera |
| Timestamp issue | Clock skew | Sync server time (NTP) |
| Reused nonce | Nonce not random | Generate unique nonce each request |
How to fix:
- Verify MAC signature calculation (see Authentication)
- Check client_id matches credentials from Paysera
- Ensure server time is synchronized
- Generate unique nonce for each request
- Verify mac_key is correct
Code Example:
// Check server time
$timestamp = time();
$serverTime = time();
$timeDiff = abs($timestamp - $serverTime);
if ($timeDiff > 300) { // 5 minutes
error_log("Warning: Server time differs by {$timeDiff} seconds");
}
// Generate unique nonce
$nonce = bin2hex(random_bytes(16));
// Calculate MAC signature correctly
$mac = calculateMacSignature($clientId, $macKey, $timestamp, $nonce, $method, $uri, $body);
forbidden
HTTP Status: 403 Forbidden
When it occurs:
- Accessing resource owned by different client
- Insufficient permissions for action
- Client type not allowed for operation
- Resource access restricted
Example:
{
"error": "forbidden",
"error_description": "This resource is assigned to other project, client has no rights to read it"
}
Common scenarios:
| Scenario | Description | Solution |
|---|---|---|
| Wrong resource | Accessing another client's transfer | Verify transfer ID belongs to you |
| No permission | Action not allowed for your client | Contact Paysera for permissions |
| Client type | Operation restricted | Check if your client type supports this |
How to fix:
- Verify you're accessing your own resources
- Check transfer ID is correct
- Contact Paysera support if you need additional permissions
- Review what operations your client is authorized for
not_found
HTTP Status: 404 Not Found
When it occurs:
- Transfer ID doesn't exist
- Resource has been deleted
- Wrong endpoint URL
- Typo in resource ID
Example:
{
"error": "not_found",
"error_description": "Transfer with id 123456789 not found"
}
How to fix:
- Verify transfer ID is correct
- Check resource hasn't been deleted
- Ensure URL path is correct
- Confirm resource exists in your system
Code Example:
try {
$transfer = getTransfer($transferId);
} catch (NotFoundException $e) {
// Transfer doesn't exist
logger()->warning("Transfer not found: {$transferId}");
// Check if it exists in your database
if (transferExistsInDatabase($transferId)) {
// It was deleted or doesn't exist in Paysera
markTransferAsNotFound($transferId);
}
}
not_acceptable
HTTP Status: 406 Not Acceptable
When it occurs:
- Unsupported Content-Type
- Unsupported Accept header
- Wrong response format requested
Example:
{
"error": "not_acceptable",
"error_description": "Requested response format not supported"
}
How to fix:
- Use
Content-Type: application/json - Use
Accept: application/json - Don't request XML or other formats
invalid_state
HTTP Status: 409 Conflict
When it occurs:
- Transfer in wrong state for requested action
- State transition not allowed
- Resource locked or being processed
Example:
{
"error": "invalid_state",
"error_description": "Cannot revoke transfer in 'done' state. Only transfers in 'waiting', 'waiting_funds', 'waiting_registration', 'waiting_password' or 'reserved' states can be revoked"
}
Valid state transitions for revoke:
| Current State | Can Revoke? | After Revoke |
|---|---|---|
waiting | ✅ Yes | revoked |
waiting_funds | ✅ Yes | revoked |
waiting_registration | ✅ Yes | revoked |
waiting_password | ✅ Yes | revoked |
reserved | ✅ Yes | revoked |
done | ❌ No | - |
failed | ❌ No | - |
rejected | ❌ No | - |
revoked | ❌ No | - |
How to fix:
- Check current transfer status before attempting action
- Only perform allowed actions for current state
- Handle state conflicts gracefully
- Wait for status changes if needed
Code Example:
$transfer = getTransfer($transferId);
// Check if revoke is possible
$canRevoke = in_array($transfer->status, [
'waiting',
'waiting_funds',
'waiting_registration',
'waiting_password',
'reserved'
]);
if (!$canRevoke) {
throw new InvalidStateException(
"Cannot revoke transfer in '{$transfer->status}' state"
);
}
// Proceed with revoke
revokeTransfer($transferId);
internal_server_error
HTTP Status: 500 Internal Server Error
When it occurs:
- Unexpected error on Paysera servers
- Database connection issues
- Service temporarily unavailable
- Unhandled exception
Example:
{
"error": "internal_server_error",
"error_description": "An unexpected error occurred. Please try again later"
}
How to handle:
- Implement retry logic with exponential backoff
- Log error details for investigation
- Contact support if error persists
- Don't retry immediately
Code Example:
function makeRequestWithRetry($url, $data, $maxRetries = 3) {
$attempt = 0;
while ($attempt < $maxRetries) {
try {
$response = makeApiRequest($url, $data);
if ($response->status === 500) {
$attempt++;
if ($attempt < $maxRetries) {
// Exponential backoff: 2^attempt seconds
$delay = pow(2, $attempt);
logger()->warning(
"Server error, retrying in {$delay}s (attempt {$attempt}/{$maxRetries})"
);
sleep($delay);
continue;
}
// Max retries reached
logger()->error("Server error persists after {$maxRetries} attempts");
alertAdmins("Persistent API error", $response);
throw new ServerErrorException("API server error");
}
return $response;
} catch (NetworkException $e) {
// Handle network errors similarly
$attempt++;
if ($attempt >= $maxRetries) throw $e;
sleep(pow(2, $attempt));
}
}
}
Transfer-Specific Error Codes
insufficient_funds
HTTP Status: 400 Bad Request
When it occurs:
- Account doesn't have enough balance
- Amount exceeds available funds
- Reserved funds not sufficient
Example:
{
"error": "insufficient_funds",
"error_description": "Account balance (50.00 EUR) is less than transfer amount (100.00 EUR)"
}
How to fix:
- Check account balance before creating transfer
- Ask user to add funds
- Reduce transfer amount
invalid_beneficiary
HTTP Status: 400 Bad Request
When it occurs:
- Beneficiary account number invalid
- IBAN format incorrect
- Beneficiary name format invalid
Example:
{
"error": "invalid_beneficiary",
"error_description": "Invalid IBAN format for beneficiary account"
}
How to fix:
- Validate IBAN format before sending
- Check beneficiary name contains only allowed characters
- Verify account number is complete
currency_not_supported
HTTP Status: 400 Bad Request
When it occurs:
- Currency code not supported
- Transfer not allowed in specified currency
Example:
{
"error": "currency_not_supported",
"error_description": "Currency XYZ is not supported"
}
Supported currencies:
- EUR
- USD
- GBP
- Contact Paysera for complete list
Error Handling Strategy
1. Categorize Errors
function handleApiError($error) {
// Client errors (4xx) - fix request
$clientErrors = [
'invalid_request',
'invalid_parameters',
'unauthorized',
'forbidden',
'not_found',
'not_acceptable',
'invalid_state'
];
// Server errors (5xx) - retry
$serverErrors = [
'internal_server_error'
];
if (in_array($error->error, $clientErrors)) {
// Fix the request
logClientError($error);
throw new ClientException($error->error_description);
}
if (in_array($error->error, $serverErrors)) {
// Retry with backoff
return retryRequest();
}
}
2. Implement Retries
async function callApiWithRetry(request, options = {}) {
const maxRetries = options.maxRetries || 3;
const retryableStatuses = [500, 502, 503, 504];
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(request.url, request.options);
// Don't retry client errors
if (response.status >= 400 && response.status < 500) {
throw await response.json();
}
// Retry server errors
if (retryableStatuses.includes(response.status)) {
if (attempt < maxRetries - 1) {
const delay = Math.pow(2, attempt) * 1000;
await sleep(delay);
continue;
}
}
return response;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
}
}
}
3. Log Everything
import logging
def handle_api_error(error_response):
error_code = error_response.get('error')
error_desc = error_response.get('error_description')
# Always log
logging.error(f"API Error: {error_code} - {error_desc}", extra={
'error_code': error_code,
'error_description': error_desc,
'timestamp': datetime.now(),
'request_id': get_request_id()
})
# Alert on critical errors
critical_errors = ['internal_server_error', 'unauthorized']
if error_code in critical_errors:
alert_admins(error_response)
# Different handling by error type
if error_code == 'invalid_parameters':
return handle_validation_error(error_response)
elif error_code == 'invalid_state':
return handle_state_conflict(error_response)
elif error_code == 'unauthorized':
return refresh_credentials()
4. User-Friendly Messages
function getUserFriendlyMessage($errorCode) {
$messages = [
'insufficient_funds' => 'You don\'t have enough funds in your account. Please add funds and try again.',
'invalid_beneficiary' => 'The recipient account details are incorrect. Please check and try again.',
'invalid_state' => 'This transfer cannot be modified in its current state.',
'internal_server_error' => 'We\'re experiencing technical difficulties. Please try again in a few moments.',
'unauthorized' => 'Your session has expired. Please log in again.',
];
return $messages[$errorCode] ?? 'An error occurred. Please try again or contact support.';
}
Testing Error Scenarios
Test Invalid Request
# Malformed JSON
curl -X POST https://wallet.paysera.com/transfer/rest/v1/transfers \
-H "Content-Type: application/json" \
-H "Authorization: MAC ..." \
-d "{invalid json"
Test Missing Parameters
# Empty body
curl -X POST https://wallet.paysera.com/transfer/rest/v1/transfers \
-H "Content-Type: application/json" \
-H "Authorization: MAC ..." \
-d "{}"
Test Invalid State
# Try to revoke completed transfer
curl -X DELETE https://wallet.paysera.com/transfer/rest/v1/transfers/123 \
-H "Authorization: MAC ..."
Quick Reference Table
| Error Code | Status | Retry? | Fix |
|---|---|---|---|
invalid_request | 400 | ❌ No | Fix request format |
invalid_parameters | 400 | ❌ No | Validate parameters |
unauthorized | 401 | ❌ No | Fix authentication |
forbidden | 403 | ❌ No | Check permissions |
not_found | 404 | ❌ No | Verify resource ID |
not_acceptable | 406 | ❌ No | Use JSON format |
invalid_state | 409 | ❌ No | Check status first |
internal_server_error | 500 | ✅ Yes | Retry with backoff |
Support
Need help with complex integrations?
Contact: tech_support@paysera.com