# Error Codes Reference

Complete list of all error codes returned by the AuxCORE API.

---

## Error Response Format

Most services return this structure:

```json
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error message"
  }
}
```

There is no `error.field` or `error.details` object. Validation messages name the offending field in the `message` text.

Three variants exist:

- **API-key failures on `/api/v1/public/*`** return `error` as a plain string: `{ "success": false, "error": "API key validation failed", "message": "..." }`. An unknown or deactivated key normally gets `API key validation failed` (the api-key service answers 401 and the gateway reports it as a validation failure); `Invalid API key` appears only when that service returns `200` without data.
- **Legacy `POST /api/v1/public/transaction` failures** use `{ "success": false, "status": "declined", "status_code": ..., "message": "...", "data": { ... } }`.
- **Some services** (merchant gateways, settlement, batch payouts, devices) return `{ "success": false, "message": "..." }` without an `error` object.

See the [Error Handling guide](../getting-started/errors.md) for examples.

---

## Authentication Errors (401)

| Code | Message | Solution |
|------|---------|----------|
| `UNAUTHORIZED` | Authentication required / invalid credentials | Send a valid JWT or API key |
| `TOKEN_EXPIRED` | Token has expired | Refresh with `POST /api/v1/auth/refresh`, or log in again |
| `INVALID_TOKEN` | Token is malformed or revoked | Log in again |
| `INVALID_API_KEY` | API key used on an admin route is not valid | Check the key, or use a JWT |
| `INVALID_CREDENTIALS` | Invalid email or password | Check login credentials |
| `API key required` / `API key validation failed` / `Invalid API key` | String `error` on `/api/v1/public/*` | Send a valid `x-api-key` |

## Tenant Errors (400, 404)

| Code | HTTP | Message | Solution |
|------|------|---------|----------|
| `TENANT_REQUIRED` | 400 | No API key and no tenant (host or `X-Tenant-ID`) could be resolved | Send a valid API key, or `X-Tenant-ID` on JWT routes |
| `TENANT_NOT_FOUND` | 404 | Tenant not found | Admin (JWT) routes only — fix `X-Tenant-ID`. `/api/v1/public/*` ignores `X-Tenant-ID` and takes the tenant from the key |

---

## Authorization Errors (403)

| Code | Message | Solution |
|------|---------|----------|
| `FORBIDDEN` | Insufficient permissions, wrong merchant, or API key not allowed on this route | Check the user's role / permissions and the key type |
| `DEVICE_NOT_ACTIVE` | Device is not activated | Complete registration / test-connection, or ask an admin to reactivate |
| `DEVICE_NOT_REGISTERED` | Device has not been registered yet | Register with TPN + pairing code first |
| `DEVICE_CONTEXT_MISMATCH` | Device token does not match the authenticated merchant | Switch merchant, then send the device token stored for that merchant |
| `DEVICE_SUSPENDED` | Device was suspended by an admin | Contact the merchant admin |
| `PROVIDER_PARAMETERS_NOT_EDITABLE` | Parameters of this provider's device are managed by the provider | Change them at the provider |
| `FIELD_LOCKED_BY_GATEWAY` | (409) A checkout field is locked by the merchant's gateway | Do not override the locked field |
| `SERVICE_UNAVAILABLE` | `Service <name> is not available for this tenant. Please upgrade your subscription.` | The tenant's plan does not include this service — this is a `403`, not a `503` |
| `MERCHANT_SCOPE_VIOLATION` | You may only access invoices for your merchant (invoice lookup) | Look up invoices of your own merchant |

SDK public keys (`lpk_live_…`) also receive `403` for `Origin required`, `Origin not allowed` and `Insufficient permissions`.

---

## Device Session Reasons (200)

`GET /api/v1/device/session` answers with `200` even when the device cannot be reused — the reason is
business state, not an error. See [Check Device Session](../mobile/device-integration.md#check-device-session).

| `reason` | Meaning | App action |
|----------|---------|------------|
| `NOT_ACTIVE` | Device was unregistered, deactivated, suspended or archived | Show Device Registration |
| `CONTEXT_MISMATCH` | The token belongs to a different merchant | Send the token stored for the current merchant |
| `REGISTERED_BY_ANOTHER_USER` | The device is owned by a different user | Show Device Registration; an admin must deactivate to hand it over |
| `DEVICE_NOT_FOUND` | The device record no longer exists | Delete the stored token and register again |

---

## Validation Errors (400)

| Code | Message | Solution |
|------|---------|----------|
| `VALIDATION_ERROR` | Field validation failed — the message names the field | Fix the invalid field |
| `PCI_SCOPE_VIOLATION` | Raw card number / CVV sent where raw card data is not allowed | Tokenize in the browser and send `pciToken` |
| `INVALID_DEVICE_TOKEN` | Device token is malformed, revoked or unknown | Register the device again |
| `DEVICE_CONTEXT_REQUIRED` | Endpoint needs a device token (`X-Device-Token`) | Send the token stored at registration |
| `UNKNOWN_EVENT_TYPE` | Webhook event name is not in the event list | Use an event from [Webhook Events](./webhook-events.md) |
| `INVALID_IDEMPOTENCY_KEY` | `Idempotency-Key` is shorter than 16 or longer than 255 characters | Use a UUID |
| `MERCHANT_ID_REQUIRED` | `merchantId is required` (invoice lookup) | Pass `merchantId` |

There are no dedicated codes for individual fields (`INVALID_EMAIL`, `INVALID_CARD_NUMBER`, `INVALID_CVV`, `INVALID_EXPIRY`, `CARD_EXPIRED` and similar). All of these surface as `VALIDATION_ERROR` or as a declined transaction.

---

## Resource Errors (404)

| Code | Message | Solution |
|------|---------|----------|
| `NOT_FOUND` | Resource not found (transactions, merchants, customers, subscriptions and others) | Verify the ID and that it belongs to your merchant |
| `INVOICE_NOT_FOUND` | Invoice not found | Verify invoice ID or link |
| `MERCHANT_NOT_FOUND` | Merchant not found | Verify merchant ID |

---

## Conflict Errors (409, 422)

| Code | HTTP | Message | Solution |
|------|------|---------|----------|
| `CONFLICT` | 409 | Resource already exists or state conflict | Use a different identifier |
| `EMAIL_IN_USE` | 409 | A user with this email already exists | Link the existing account instead |
| `DISPUTE_NOT_OPEN` | 409 | The dispute can no longer be responded to | Check the dispute status |
| `IDEMPOTENCY_REQUEST_IN_PROGRESS` | 409 | The first request with this key has not finished | Wait, then retry with the same key |
| `IDEMPOTENCY_KEY_REUSED` | 422 | Same key, different request body | Use a new key for a new request |
| `VELOCITY_REVIEW_PENDING` | 409 | The transaction is held for Transaction Rules review | The merchant approves or denies the review in the portal; void, refund, credit and capture are refused until then |

Void, refund and capture rule violations (already voided, already refunded, already captured, not capturable, amount too large) are **not** separate codes. They return `400 VALIDATION_ERROR` with a descriptive message, described under Business Logic Errors below.

---

## Payment Errors

### Transaction Declined

A declined card sale on `POST /api/v1/public/transaction` returns **HTTP 402** with `Status: "9"` and a numeric `ResponseCode` — there is no `TRANSACTION_DECLINED`, `CARD_DECLINED` or `INSUFFICIENT_FUNDS` error code. See Decline Codes below.

### AVS/CVV Declines

AVS and CVV rule failures are declines with an internal response code and message, not separate error codes:

| ResponseCode | Message | Solution |
|--------------|---------|----------|
| `220` | Do Not Honor – Address Verification Failed | Verify the billing address |
| `225` | Do Not Honor – CVV Required | Collect the CVV (required for raw-card payments) |

### Transaction Rules Declines

A merchant's Transaction Rule with the Deny action declines the payment before it reaches the processor:

| ResponseCode | Message | Solution |
|--------------|---------|----------|
| `VELOCITY_DENIED` | `Declined by Transaction Rules: {rule name}` | The payment matched one of the merchant's Transaction Rules (too many attempts from the same IP or customer, or an amount condition). Do not retry right away |

### Gateway Errors

| Code | HTTP | Message | Solution |
|------|------|---------|----------|
| `GATEWAY_ERROR` | 5xx | Payment gateway error | Retry or contact support |
| `BAD_GATEWAY` | 502 | Error communicating with service (API gateway to a downstream service) | Retry |
| `GATEWAY_TIMEOUT` | 5xx | Gateway timeout | Retry request |

### Limit Errors

| Code | HTTP | Message | Solution |
|------|------|---------|----------|
| `LIMIT_EXCEEDED` | 422 | Amount would exceed a gateway, device or transaction limit | Reduce the amount or contact support to adjust limits |
| `WATCHDOG_LIMIT_EXCEEDED` | 422 | `Transaction blocked by WatchDog volume limit` | Volume exceeds configured limits — contact support |

### Cash Payment Errors

| Code | HTTP | Message | Solution |
|------|------|---------|----------|
| `VALIDATION_ERROR` | 400 | `Cash payment only supports SALE transactions, received: {type}` | Use `POST /api/v1/transactions/sale` for cash payments — `auth` and `capture` are not supported |
| `VALIDATION_ERROR` | 400 | `Void is not supported for Cash transactions...` | Cash cannot be voided; issue a full refund |
| `VALIDATION_ERROR` | 400 | `Cash refunds support full refund only` | Refund the full amount |

---

## Business Logic Errors

These all return `400 VALIDATION_ERROR` (or `404 NOT_FOUND`); match on the `message` text rather than a code.

### Voids, Captures, Refunds

| Message (abridged) | Solution |
|--------------------|----------|
| `Only approved auth transactions can be captured` | Capture only approved authorizations |
| `…already been settled…issue a refund` (void) | Refund instead of voiding a settled transaction |
| `Refund amount ($x) exceeds remaining refundable amount ($y)…` | Reduce the refund amount |
| `Gateway reversal was unsuccessful…` (void) | The gateway rejected the void; it may have settled — refund instead |

A refund that the processor declines still returns `201` with `data.status: "declined"`. Gateway declines are added to the Refund Decline Queue, but nothing retries them automatically — call `POST /api/v1/refund-decline-queue/:id/retry` or `/process-retries` (see [Refund Queue](../advanced/refund-queue.md)).

### Invoices

| Message (abridged) | Solution |
|--------------------|----------|
| `Cannot resend a paid, cancelled or expired invoice` | Only unpaid invoices can be sent |
| Payment amount > balance / below the minimum payment | Use `POST /invoices/:id/payment-preflight` to validate first |

### Subscriptions

| Message (abridged) | Solution |
|--------------------|----------|
| `No failed payments to retry` | `retry-failed` needs a subscription with failed payments |

---

## Rate Limiting Errors (429)

| Code | Message | Solution |
|------|---------|----------|
| `RATE_LIMITED` | Too many requests from this IP. Try again later. | Wait for `Retry-After` seconds and retry with backoff |

The gateway's general limiter answers with the plain-text body `Too many requests, please try again later` instead of JSON. Both include `RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset` (seconds) and `Retry-After` headers. See [Rate Limits](../getting-started/rate-limits.md).

```json
{
  "success": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "Too many requests from this IP. Try again later."
  }
}
```

---

## Server Errors (5xx)

| Code | Message | Solution |
|------|---------|----------|
| `INTERNAL_ERROR` | Internal server error | Retry or contact support |
| `SERVICE_UNAVAILABLE` | (503) Downstream service temporarily unavailable. The plan/feature gate uses the same code with `403` — do not retry that one | Retry with backoff (503 only) |
| `IDEMPOTENCY_UNAVAILABLE` | (503) The idempotency store is unavailable, so the request was not processed | Retry with the same `Idempotency-Key` |
| `GATEWAY_TIMEOUT` | (504) A downstream service did not answer in time | Retry; check the transaction before re-charging |
| `FEE_PREVIEW_FAILED` | (502) Fee preview could not be computed | Retry |
| `SETTLEMENT_FAILED` | (422) Batch settle was rejected by the processor | Check the batch, then retry or resubmit |

If the gateway cannot reach a downstream service it returns `500 { "error": "Service unavailable", "message": "..." }`.

---

## Decline Codes (Response Codes)

Declines are reported in `ResponseCode` / `ResponseMessage` using NMI-compatible numeric codes. Processor-native codes (for example TSYS `05`) are normalized to these. `100` is approval.

| Code | Reason | Action |
|------|--------|--------|
| `200` | Declined by processor | Try a different card |
| `201` | Do not honor | Contact issuer |
| `202` | Insufficient funds | Customer needs to add funds |
| `203` | Over limit | Reduce amount |
| `204` | Transaction not allowed | Try a different card |
| `220` | Incorrect payment information (also AVS failure rule) | Verify card number and address |
| `221` / `222` | No such card issuer / no card number on file | Verify card number |
| `223` | Expired card | Use non-expired card |
| `224` | Invalid expiration date | Check expiration |
| `225` | Invalid card security code | Verify CVV |
| `226` | Invalid PIN | Re-enter PIN |
| `240` | Call issuer | Customer should call the bank |
| `250` / `251` / `252` / `253` | Pick up, lost, stolen or fraudulent card | Use a different card |
| `260`–`264` | Declined with further instructions (stop recurring, update cardholder data, retry later) | See `ResponseMessage` |
| `300` | Rejected by gateway | Check request and gateway config |
| `400` / `410` / `411` | Processor error / invalid or inactive merchant configuration | Contact support |
| `420` / `421` | Communication error | Retry |
| `430` | Duplicate transaction at processor | Check for an existing transaction |
| `440` / `441` | Processor format error / invalid transaction information | Check request |
| `460` / `461` | Feature or card type not supported | Use a supported card brand |

Soft declines (retry may succeed) include the `26x` and communication-error codes.

---

## ACH Return Codes

ACH returns are delivered by the `transaction.returned` webhook. The NACHA R-code appears in `data.responseText`.

| Code | Description | Action |
|------|-------------|--------|
| `R01` | Insufficient funds | Customer needs funds |
| `R02` | Account closed | Get new account info |
| `R03` | No account found | Verify account number |
| `R04` | Invalid account number | Check routing/account |
| `R05` | Unauthorized debit | Customer must authorize |
| `R07` | Authorization revoked | Get new authorization |
| `R10` | Not authorized | Customer needs to authorize |
| `R29` | Corporate customer advises not authorized | Contact customer |

---

## Error Handling Best Practices

### Retryable Errors

These errors should be retried:
- `GATEWAY_TIMEOUT`
- `SERVICE_UNAVAILABLE` (only with HTTP 503)
- `RATE_LIMITED`
- 500-level errors

**Use exponential backoff:**
```javascript
async function retryWithBackoff(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (!isRetryable(error) || i === maxRetries - 1) {
        throw error;
      }
      await sleep(Math.min(1000 * Math.pow(2, i), 10000));
    }
  }
}
```

### Non-Retryable Errors

Don't retry these:
- `VALIDATION_ERROR`
- `UNAUTHORIZED`
- `NOT_FOUND`
- `FORBIDDEN`
- A `402` decline (`Status: "9"`)

Fix the issue first, then retry.

---

## Testing Error Scenarios

### Test Card Numbers for Errors

| Card Number | Result |
|-------------|--------|
| 4000000000000002 | Decline (enter it in the hosted card fields, then charge the `pciToken`; the result may vary by processor) |

Other decline test numbers are not documented here. Use the amounts and cards listed in [Testing](../getting-started/testing.md).

### Test API Errors

```bash
# Test validation error (Public API)
curl -X POST https://uat.auxcore.net/api/v1/public/transaction \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"Amount": -100, "pciToken": "pci_tok_9f2c4e7a1b3d"}'

# Test not found error (Public API)
curl https://uat.auxcore.net/api/v1/public/transaction/invalid_id \
  -H "x-api-key: YOUR_API_KEY"

# Test unauthorized error
curl https://uat.auxcore.net/api/v1/public/transaction-list \
  -H "x-api-key: invalid_key"
```

---

## Next Steps

- [Error handling guide →](../getting-started/errors.md)
- [All endpoints →](./endpoints.md)
- [Webhook events →](./webhook-events.md)

---

**Need help?** Contact support@auxcore.net
