Error Reference
Complete API error reference. HTTP status codes, error response format, common error messages, rate limit headers, and how SDKs map errors to typed exceptions.
Error Reference
All PayzCore API errors follow a consistent JSON format. This page documents every error status code, the response structure, common error messages, and how to handle them.
Response Format
Every error response includes an error field with a human-readable message. Some errors include additional fields for context.
Standard Error
{
"error": "Description of what went wrong"
}Validation Error
When the request body fails validation (wrong type, missing required field, value out of range), the API returns a single generic message. It does not list the failing fields, so check your request against the API Reference.
{
"error": "Invalid request body"
}Rate Limit Error (with plan info)
{
"error": "Daily API call limit exceeded. Upgrade your plan for unlimited access.",
"limit": 500,
"plan": "free"
}HTTP Status Codes
400 Bad Request
Validation errors caused by invalid parameters, missing required fields, or unsupported values.
| Error Message | Cause |
|---|---|
Invalid request body | JSON parse error or schema validation failure (wrong type, missing required field, value out of range). |
USDC is not supported on TRC20 | Unsupported network+token combination. See Supported Networks. |
Amount must be at least $X.XX (project minimum) | Payment amount is below the project's configured minAmount. |
Project has no wallet assigned | The project needs a wallet before creating payments. |
Wallet not found | The wallet linked to the project was deleted or is missing. |
Wallet has no xPub configured for this network | The wallet is missing the xPub key for the requested network's address family (tron or evm). |
Amount has more decimal places than this project's micro-amount precision allows | Pool + micro_amount mode: the base amount has more decimals than the project's micro-amount precision (send e.g. 50 or 50.5, PayzCore adds the offset). |
Specified address does not belong to this project's wallet | The address parameter (dedicated mode) references an address not in the wallet's static address pool. |
No addresses available for this network. Contact merchant. | No static address is registered for this network, or all are already assigned (dedicated mode). |
Invalid limit or offset | Pagination parameters must be positive integers. limit max is 100. |
Invalid status filter | Status must be one of: pending, confirming, partial, paid, overpaid, expired, cancelled. |
Invalid created_after | Date parameter is not a valid ISO 8601 string. |
Invalid updated_after | Date parameter is not a valid ISO 8601 string. |
How to fix: Compare your request with the API Reference (types, required fields, limits). Correct the request parameters and retry.
401 Unauthorized
Authentication failed. The API key is missing, invalid, or malformed.
| Error Message | Cause |
|---|---|
Missing x-api-key header | The x-api-key header was not included in the request. |
Invalid API key | The API key does not match any active project. |
Too many requests with unknown API keys from one IP (30 per minute) return 429 Too many invalid API key attempts. Try again later. instead.
How to fix: Verify your API key is correct and included in the x-api-key header. API keys start with pk_live_. If you lost your key, the project owner can regenerate it from the project settings.
403 Forbidden
The request was authenticated but is not allowed due to access restrictions.
| Error Message | Cause |
|---|---|
Project is deactivated | An admin has deactivated this project. |
Account suspended. Contact support. | The project owner's account has been suspended. |
Account is no longer active. | The project owner's account was deleted or closed. |
How to fix: Contact PayzCore support or your account administrator. Deactivated projects cannot make API calls until reactivated.
404 Not Found
The requested resource does not exist or is not accessible by this API key.
| Error Message | Cause |
|---|---|
Payment not found | The payment ID does not exist or belongs to a different project. |
How to fix: Verify the payment ID is correct. Each project can only access its own payments.
409 Conflict
The request conflicts with an existing resource.
| Endpoint | Error Message | Cause |
|---|---|---|
POST /v1/payments | Payment already exists with different amount | A pending payment with the same identifiers exists but with another amount. The body also has existing_amount. |
POST /v1/payments | external_order_id is already in use with a different external_ref | The external_order_id belongs to another customer reference. |
POST /v1/payments | This address is already assigned to a different customer | Dedicated mode: the address you passed is assigned to another external_ref. |
POST /v1/payments | This customer already has a payment in progress on this network. Cancel it or wait for it to complete. | per_user / dedicated modes: the same external_ref already has a pending/confirming/partial payment on this network+token for a different order (other amount or external_order_id). The body also has existing_payment_id and existing_amount. Cancel it (PATCH) or reuse it. |
PATCH /v1/payments/:id | Cannot cancel payment with status "X". Only pending payments can be cancelled. | Only pending payments can be cancelled. |
PATCH /v1/payments/:id | Payment status changed before cancellation could complete. Please refresh and try again. | The payment changed state while it was being cancelled. |
POST /v1/payments/:id/confirm | Transaction hash already submitted and pending confirmation | A tx hash was already submitted for this payment. The body also has submitted_tx_hash. |
POST /v1/payments/:id/confirm | This transaction hash is already submitted for another payment / This transaction hash is already associated with another payment | The same tx hash cannot confirm two payments. |
POST /v1/payments/:id/confirm | Payment already finalized | The payment reached a final status while the hash was being checked. |
Repeating a request is not a 409. When you send the same external_order_id + external_ref + network + token (and amount) again, PayzCore returns 200 with the existing payment and "existing": true. This makes retries safe. The SDKs map every 409 above to IdempotencyError / IdempotencyException; read its message for the exact reason.
429 Too Many Requests
Rate limit exceeded. There are two types of rate limits:
Per-Minute Rate Limit (burst protection)
Limits the number of requests per API key per minute. Default: 60 requests/minute.
{
"error": "Rate limit exceeded. Try again later."
}Response headers (sent only on 429 responses, not on successful ones):
| Header | Value | Description |
|---|---|---|
X-RateLimit-Limit | 60 | Maximum requests per window |
X-RateLimit-Remaining | 0 | Remaining requests in current window |
X-RateLimit-Reset | 1708444800 | Unix timestamp (seconds) when the window resets |
Daily Limit (plan-based)
Limits total API calls per day based on your plan.
{
"error": "Daily API call limit exceeded. Upgrade your plan for unlimited access.",
"limit": 500,
"plan": "free"
}Response headers (only on 429 responses):
| Header | Value | Description |
|---|---|---|
X-RateLimit-Limit | 500 | Daily limit for your plan |
X-RateLimit-Remaining | 0 | Remaining calls today |
X-RateLimit-Reset | 1708473600 | Unix timestamp (seconds) when the daily counter resets (midnight UTC) |
X-RateLimit-Daily | true | Indicates this is a daily limit (not per-minute) |
Plan limits:
| Plan | API Calls/Day | Webhooks/Day |
|---|---|---|
| Free | 500 | 50 |
| Pro | 5,000 | 1,000 |
Admin-configured custom overrides may apply per user.
Capacity Limit (pool mode)
When using pool+micro_amount mode, all available offsets for the requested amount may be exhausted. This 429 has no X-RateLimit-* headers:
{
"error": "Payment capacity exceeded. Please try again later."
}How to fix for per-minute limits: Wait for the reset time indicated in the X-RateLimit-Reset header. Implement exponential backoff in your client.
How to fix for daily limits: Wait until midnight UTC, or upgrade to the Pro plan for higher limits.
How to fix for capacity limits: Wait for existing payments on the pool to resolve (paid/expired), or add more static addresses.
500 Internal Server Error
An unexpected error occurred on the server.
{
"error": "Failed to create payment"
}How to fix: Retry the request with exponential backoff. If the error persists, check the health endpoint and contact support.
Recommended retry strategy:
| Attempt | Delay |
|---|---|
| 1 | 1 second |
| 2 | 2 seconds |
| 3 | 4 seconds |
| 4 | 8 seconds |
| 5 | Give up, alert your team |
SDK Error Handling
The official SDKs map HTTP errors to typed exception classes, making it easy to handle specific error types.
Node.js SDK
import {
PayzCoreError,
ValidationError,
AuthenticationError,
ForbiddenError,
NotFoundError,
RateLimitError,
IdempotencyError,
} from '@payzcore/node'
try {
const payment = await payzcore.payments.create({
amount: 50,
network: 'TRC20',
externalRef: 'customer-123',
})
} catch (err) {
if (err instanceof ValidationError) {
// 400 - Invalid parameters
console.error('Validation:', err.message)
} else if (err instanceof AuthenticationError) {
// 401 - Invalid API key
console.error('Auth:', err.message)
} else if (err instanceof ForbiddenError) {
// 403 - Project inactive or account suspended
console.error('Forbidden:', err.message)
} else if (err instanceof NotFoundError) {
// 404 - Resource not found
console.error('Not found:', err.message)
} else if (err instanceof RateLimitError) {
// 429 - Rate limited
console.error('Rate limited:', err.message)
// retryAfter is a Unix timestamp (seconds), not a duration
const waitSeconds = Math.max(0, (err.retryAfter ?? 0) - Math.floor(Date.now() / 1000))
console.error('Retry in', waitSeconds, 'seconds. Daily limit:', err.isDaily)
} else if (err instanceof IdempotencyError) {
// 409 - Conflict (see the 409 table above)
console.error('Conflict:', err.message)
} else if (err instanceof PayzCoreError) {
// Other API errors (500, etc.)
console.error('API error:', err.status, err.message)
}
}Error Class Hierarchy
| HTTP Status | SDK Class | Properties |
|---|---|---|
| 400 | ValidationError | message, status, code |
| 401 | AuthenticationError | message, status, code |
| 403 | ForbiddenError | message, status, code |
| 404 | NotFoundError | message, status, code |
| 409 | IdempotencyError | message, status, code |
| 429 | RateLimitError | message, status, code, retryAfter (Unix timestamp in seconds, from X-RateLimit-Reset), isDaily (boolean) |
| Other | PayzCoreError | message, status, code |
All error classes extend PayzCoreError, which extends Error. You can catch PayzCoreError as a catch-all for any API error.
Python SDK
import time
from payzcore import PayzCoreError, ValidationError, RateLimitError
try:
payment = client.payments.create(
amount=50, network="TRC20", external_ref="customer-123"
)
except ValidationError as e:
print(f"Validation: {e.message}")
except RateLimitError as e:
# retry_after is a Unix timestamp (seconds), not a duration
wait = max(0, (e.retry_after or 0) - int(time.time()))
print(f"Rate limited, retry in {wait}s")
except PayzCoreError as e:
print(f"API error {e.status}: {e.message}")PHP SDK
use PayzCore\Exceptions\ValidationException;
use PayzCore\Exceptions\RateLimitException;
use PayzCore\Exceptions\PayzCoreException;
try {
$payment = $client->payments->create([
'amount' => 50, 'network' => 'TRC20', 'externalRef' => 'customer-123'
]);
} catch (ValidationException $e) {
echo "Validation: " . $e->getMessage();
} catch (RateLimitException $e) {
// getRetryAfter() is a Unix timestamp (seconds), not a duration
$wait = max(0, ($e->getRetryAfter() ?? time()) - time());
echo "Rate limited, retry in {$wait}s";
} catch (PayzCoreException $e) {
echo "API error {$e->getStatusCode()}: {$e->getMessage()}";
}Best Practices
- Always check the status code first. Different status codes require different handling strategies.
- Read the
errormessage on 400 errors. Validation failures returnInvalid request bodywithout field details, so compare your request with the API Reference. - Implement retry logic for 429 and 500 errors. Use the
X-RateLimit-Resetheader for rate limits and exponential backoff for server errors. - Do not retry 400, 401, 403, or 404 errors. These indicate issues that need to be fixed in your code or configuration, not transient failures.
- Use
external_order_idfor idempotency. This prevents duplicate payment creation during retries and returns the existing payment safely. - Log error responses. Store the full error response body for debugging and support requests.
- Use typed SDK exceptions. The official SDKs provide specific error classes that make error handling clean and type-safe.
Rate Limits & Plans
PayzCore enforces burst rate limits (per-IP, per-API-key) and daily plan-based limits. Free plan: 500 API calls/day, Pro plan: 5,000/day. Daily counters reset at midnight UTC.
Sweeping Funds
Understanding fund collection from monitored addresses. PayzCore is non-custodial and does not perform sweeps.