Skip to main content

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"
}
FieldTypeAlways PresentDescription
errorstring✅ YesMachine-readable error code
error_descriptionstringUsuallyHuman-readable error message
error_uristringSometimesLink to error documentation

HTTP Status Codes

StatusCategoryMeaning
200SuccessRequest completed successfully
400Client ErrorBad request or invalid parameters
401Client ErrorAuthentication failed
403Client ErrorAccess forbidden
404Client ErrorResource not found
406Client ErrorNot acceptable format
409Client ErrorResource state conflict
500Server ErrorInternal 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:

ParameterErrorSolution
amountMissing or invalidProvide positive decimal number
currencyInvalid codeUse 3-letter ISO code (EUR, USD, etc.)
account_numberInvalid formatCheck IBAN format
callback.urlInvalid URLUse 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:

IssueCauseSolution
Invalid signatureWrong MAC calculationVerify MAC algorithm and key
Wrong client_idTypo or wrong credentialsCheck client ID from Paysera
Timestamp issueClock skewSync server time (NTP)
Reused nonceNonce not randomGenerate 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:

ScenarioDescriptionSolution
Wrong resourceAccessing another client's transferVerify transfer ID belongs to you
No permissionAction not allowed for your clientContact Paysera for permissions
Client typeOperation restrictedCheck 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 StateCan Revoke?After Revoke
waiting✅ Yesrevoked
waiting_funds✅ Yesrevoked
waiting_registration✅ Yesrevoked
waiting_password✅ Yesrevoked
reserved✅ Yesrevoked
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 CodeStatusRetry?Fix
invalid_request400❌ NoFix request format
invalid_parameters400❌ NoValidate parameters
unauthorized401❌ NoFix authentication
forbidden403❌ NoCheck permissions
not_found404❌ NoVerify resource ID
not_acceptable406❌ NoUse JSON format
invalid_state409❌ NoCheck status first
internal_server_error500✅ YesRetry with backoff

Support

Need help with complex integrations?

Contact: tech_support@paysera.com

Additional Resources