On this page
No Headings
Last updated: June 17, 2026
Estimated time: 7 minutes
All Invoicing API error responses follow a consistent structure. This guide covers error codes, retry guidance, and client handling patterns.
All error responses follow this structure.
{
"name": "ERROR_CODE",
"message": "Human-readable description.",
"debug_id": "abc123def456",
"details": [
{
"field": "/items/0/unit_amount",
"issue": "INVALID_FORMAT",
"description": "The value must be a valid currency amount.",
"value": "$value",
"path": "body"
}
],
"links": [
{
"rel": "information_link",
"href": "https://developer.paypal.com/invoicing/errors/"
}
]
}| Field | Type | Description |
|---|---|---|
name | string | Stable error code, for example, INVALID_REQUEST, AUTHENTICATION_FAILURE. |
message | string | Human-readable summary of the error. |
debug_id | string | Unique request identifier for support diagnostics. |
details | array | Field-level validation errors for 4xx responses. |
details[].field | string | JSON path to the problematic field. |
details[].issue | string | Specific validation issue, for example, INVALID_FORMAT, REQUIRED_FIELD. |
details[].description | string | Resolution guidance. |
links | array | Links to documentation or support resources. |
The following table lists all error codes, their HTTP status, and whether the request can be retried.
| Status | Error code | Description | Retryable |
|---|---|---|---|
400 | INVALID_REQUEST | Malformed request body, missing required fields, or invalid field values. | No |
401 | AUTHENTICATION_FAILURE | Invalid or missing access token. The token may have expired. | Yes, after refreshing the token. |
403 | AUTHORIZATION_FAILURE | Valid token, but insufficient permissions for this operation. | No |
404 | NOT_FOUND | Invoice, template, or resource doesn't exist. | No |
409 | CONFLICT | Operation conflicts with the current resource state, for example, invoice already sent or recipient changed twice in 72 hours. | No |
422 | UNPROCESSABLE_ENTITY | Request is well-formed but violates business rules, for example, payment amount exceeds invoice total. | No |
429 | RATE_LIMIT_EXCEEDED | Too many requests in the time window. | Yes, with exponential backoff. |
500 | INTERNAL_ERROR | Unexpected server error. | Yes, with exponential backoff. |
502 | TEMPORARILY_UNAVAILABLE | Service temporarily unavailable. | Yes, with exponential backoff. |
503 | SYSTEM_ERROR | Scheduled maintenance or service degradation. | Yes, with exponential backoff. Check the status page. |
The following scenarios show common errors and how to resolve them.
{
"name": "INVALID_REQUEST",
"message": "The merchant account specified must be in good standing with PayPal.",
"debug_id": "xyz789abc",
"details": [
{
"field": "/invoicer",
"issue": "INVALID_ACCOUNT",
"description": "The specified merchant account does not exist or is not in good standing."
}
]
}To resolve this, verify the invoicer's email address and PayPal account status. Confirm the account isn't suspended or limited.
If you try to record a payment on an invoice that PayPal has already processed, you receive the following error.
{
"name": "UNPROCESSABLE_ENTITY",
"message": "The requested action could not be performed, semantically incorrect, or failed business validation.",
"debug_id": "2493c71e2152b",
"details": [
{
"field": "invoiceId",
"value": "INV2-5GE6-2KVL-XPUN-G5LQ",
"location": "path",
"issue": "INVOICE_ALREADY_SENT",
"description": "The invoice is already sent."
}
],
"links": [
{
"href": "https://developer.paypal.com/invoicing/errors/",
"method": "GET"
}
]
}To resolve this, verify the invoice status using GET. If the status is already PAID or PARTIALLY_PAID, PayPal automatically records the payment. Use the record payment endpoint only for offline payments received through channels other than PayPal.
The following guidance covers how to handle different error types in your integration.
For 5xx and 429 errors, implement exponential backoff with jitter.
async function makeRequestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.ok) {
return response;
}
if (response.status === 429 || response.status >= 500) {
const delayMs = Math.pow(2, attempt - 1) * 1000 + Math.random() * 1000;
console.log(`Retrying in ${delayMs}ms...`);
await new Promise(resolve => setTimeout(resolve, delayMs));
continue;
}
// Non-retryable error
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
} catch (error) {
if (attempt === maxRetries) throw error;
}
}
}For 4xx errors other than 401 and 429, return the error to the user with the debug_id. Don't retry. Use the details array to provide field-level feedback.
For 401 errors, refresh the access token and retry the request once. If the second attempt also returns 401, return the error to the user.
debug_id for troubleshooting.5xx errors that persist for more than 5 minutes.403 errors as a configuration issue and review your OAuth scopes.429 patterns to understand API usage and adjust request rates.