> ## Documentation Index
> Fetch the complete documentation index at: https://docs.numeral.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Codes

> Complete list of error codes and their meanings for API version 2026-11-01

# Error Codes

Starting with API version `2026-01-01`, error responses use a simplified, consistent format. Version `2026-03-01` adds IP resolution error types. Version `2026-11-01` doesn't add or change any errors.

## Error Response Format

All errors return a JSON object with three fields:

```json theme={null}
{
  "code": 400,
  "type": "ZIP_STATE_MISMATCH",
  "message": "Unable to create calculation. address_zip_code 92037 not found in the address_province TN. Ensure zip is up to date and formed correctly."
}
```

| Field | Type | Description |
| - | - | - |
| `code` | integer | HTTP status code |
| `type` | string | Machine-readable error type (use this for programmatic handling) |
| `message` | string | Human-readable description of the error |

> [!NOTE]
>
> Previous API versions used a nested `error` object. The 2026-01-01+ format is flattened for easier parsing.

***

## Error Types Reference

### Validation Errors

These errors occur when request data is invalid or malformed.

| Type | Description |
| - | - |
| `ERROR` | Generic error |
| `MISSING_FIELD` | A required field was not provided |
| `INCORRECT_TYPE` | A field has the wrong data type |
| `UNRECOGNIZED_FIELD` | An unknown field was included in the request |
| `NEGATIVE_NUMBER_REQUIRED` | Field requires a negative number |
| `POSITIVE_NUMBER_REQUIRED` | Field requires a positive number |
| `INVALID_EMAIL` | Email address format is invalid |

### Address Errors

These errors relate to address validation issues.

| Type | Description |
| - | - |
| `MALFORMED_ADDRESS` | Address format is invalid or incomplete |
| `ZIP_STATE_MISMATCH` | ZIP/postal code does not match the provided state/province |
| `MISSING_ORIGIN_ADDRESS` | Origin address is required but not provided (check merchant default address) |
| `INVALID_COUNTRY_CODE` | Country code is not recognized (use ISO 3166-1 alpha-2) |

### IP Resolution Errors

These errors relate to IP-based tax resolution (new in 2026-03-01).

| Type | HTTP Code | Description |
| - | - | - |
| `INVALID_IP_FORMAT` | 400 | `ip.value` must be a valid IPv4 or IPv6 address |
| `IP_RESOLUTION_FAILED` | 422 | Unable to resolve IP address to a country. Provide a full address in `customer.address`. |
| `IP_RESOLUTION_INSUFFICIENT_US` | 422 | IP resolved to a US state but could not determine a postal code. Provide `address_postal_code` and `address_province` in `customer.address`. |
| `IP_RESOLUTION_INSUFFICIENT_CA` | 422 | IP resolved to CA but could not determine a province. Provide `address_province` in `customer.address`. |

> [!NOTE]
>
> IP resolution errors only occur when using `customer.ip` with `resolution: "strict"` (the default). Use `"best_effort"` or `"zero"` to avoid these errors and receive zero-rate responses instead.

### Product Errors

These errors relate to product operations.

| Type | Description |
| - | - |
| `PRODUCT_NOT_FOUND` | The specified product ID does not exist |
| `DUPLICATE_PRODUCT` | A product with this reference ID already exists |
| `INVALID_TAX_CODE` | The product tax code is not valid |
| `INVALID_PRODUCT_CATEGORY` | The product category is not recognized |

### Customer Errors

These errors relate to customer operations.

| Type | Description |
| - | - |
| `CUSTOMER_NOT_FOUND` | The specified customer ID does not exist |
| `DUPLICATE_CUSTOMER` | A customer with this reference ID already exists (returns HTTP 400) |

### Merchant Errors

These errors relate to merchant operations (new in 2026-01-01).

| Type | Description |
| - | - |
| `MERCHANT_NOT_FOUND` | The specified merchant ID does not exist |
| `DUPLICATE_MERCHANT` | A merchant with this reference ID already exists |
| `MERCHANT_CREATION_FAILED` | Failed to create the merchant |
| `MERCHANT_UPDATE_FAILED` | Failed to update the merchant |

### Calculation Errors

These errors relate to tax calculation operations.

| Type | Description |
| - | - |
| `CALCULATION_NOT_FOUND` | The specified calculation ID does not exist |
| `CALCULATION_EXPIRED` | The calculation has expired and can no longer be used |
| `INVALID_CURRENCY_CODE` | Currency code is not supported (see [supported currencies](https://docs.numeral.com/essentials/currencies.md)) |

### Transaction Errors

These errors relate to transaction operations.

| Type | Description |
| - | - |
| `TRANSACTION_NOT_FOUND` | The specified transaction ID does not exist |
| `TRANSACTION_ALREADY_EXISTS` | A transaction with this calculation ID already exists |
| `REFERENCE_ORDER_ID_ALREADY_EXISTS` | A transaction with this reference order ID already exists |

### Certificate Errors

These errors relate to exemption certificate operations.

| Type | Description |
| - | - |
| `CERTIFICATE_NOT_FOUND` | The specified certificate ID does not exist (or belongs to a different account) |

### Certificate Request Errors

These errors relate to certificate-request operations.

| Type | HTTP Code | Description |
| - | - | - |
| `CERTIFICATE_REQUEST_NOT_FOUND` | 404 | The specified certificate request ID does not exist (or belongs to a different account) |
| `REQUEST_ALREADY_FULFILLED` | 409 | Cannot cancel a request that has already been fulfilled. Fetch the resulting certificate via `/tax/certificates/{certificate_id}` instead. |
| `REQUEST_INVALID` | 409 | The certificate request is in a terminal `invalid` state and cannot be canceled. |

### Certificate Upload Errors

These errors relate to submitting certificate documents via `POST /tax/certificates` and reading uploads via `/tax/certificate-uploads`.

| Type | HTTP Code | Description |
| - | - | - |
| `INVALID_DOCUMENT_URL` | 400 | `document_url` is not a valid URL, is not `https`, or points to a blocked, unresolvable, or private host |
| `DOCUMENT_FETCH_FAILED` | 400 | The document could not be downloaded — network error, too many redirects, HTTP error from the document host, or empty file |
| `DOCUMENT_URL_UNAUTHORIZED` | 422 | The document host returned 401 or 403, most commonly an expired presigned URL. Mint a fresh URL and retry. |
| `UNSUPPORTED_FILE_TYPE` | 400 | The document is not a PDF, PNG, or JPEG (detected from content). Share/viewer links that return HTML fail with this type. |
| `FILE_TOO_LARGE` | 400 | The document exceeds 10 MB |
| `BUYER_IDENTITY_CONFLICT` | 409 | `customer.id` and `customer.reference_customer_id` resolve to different customers. Nothing was written. |
| `CERTIFICATE_UPLOAD_NOT_FOUND` | 404 | The specified upload ID does not exist, is malformed, or belongs to a different account |
| `TESTMODE_NOT_SUPPORTED` | 400 | A `sk_test_*` key was used on a live-only endpoint |
| `NUMERAL_ERROR` | 500 | Internal error. For `POST /tax/certificates`, the upload was stored but processing could not start — retry the submission. |

### Exchange Rate Errors

These errors relate to the [Get Exchange Rate](https://docs.numeral.com/api-reference/v2026-11-01/endpoint/get_exchange_rate) endpoint. If a request has more than one error, only the first is returned, in the order listed below.

| Type | HTTP Code | Description |
| - | - | - |
| `UNRECOGNIZED_FIELD` | 400 | The request includes a query parameter other than `date`, `base_currency`, or `target_currency` |
| `MISSING_FIELD` | 400 | A required query parameter is missing or empty |
| `INVALID_DATE` | 400 | `date` is not a valid calendar date in `YYYY-MM-DD` format |
| `DATE_OUT_OF_RANGE` | 400 | `date` is later than the current date in UTC |
| `INVALID_CURRENCY_CODE` | 400 | `base_currency` or `target_currency` is not a three-letter code |
| `UNSUPPORTED_CURRENCY_CODE` | 400 | `base_currency` or `target_currency` is not a [supported currency](https://docs.numeral.com/essentials/currencies.md) |
| `EXCHANGE_RATE_NOT_FOUND` | 404 | No exchange rate is available for the currency pair on the given date |

***

## HTTP Status Codes

| Code | Meaning |
| - | - |
| `201` | Created - The resource was accepted (e.g., a certificate upload) |
| `400` | Bad Request - Invalid parameters, validation error, or resource already exists (duplicates) |
| `401` | Unauthorized - Invalid or missing API key |
| `404` | Not Found - Resource does not exist |
| `409` | Conflict - the resource is in a state that cannot accept this operation (e.g., canceling an already-fulfilled certificate request), or two identifiers in the request disagree (`BUYER_IDENTITY_CONFLICT`) |
| `422` | Unprocessable Entity - Request understood but cannot be processed (includes IP resolution errors and `DOCUMENT_URL_UNAUTHORIZED`) |
| `429` | Too Many Requests - Rate limit exceeded |
| `500` | Internal Server Error - Something went wrong on our end |

***

## Handling Errors

### Example Error Handling

```javascript theme={null}
try {
  const response = await fetch('https://api.numeralhq.com/tax/calculations', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer sk_test_xxx',
      'X-API-Version': '2026-11-01',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(calculationData)
  });

  if (!response.ok) {
    const error = await response.json();

    switch (error.type) {
      case 'ZIP_STATE_MISMATCH':
        // Prompt user to verify their address
        break;
      case 'PRODUCT_NOT_FOUND':
        // Create the product first
        break;
      case 'CALCULATION_EXPIRED':
        // Request a new calculation
        break;
      case 'IP_RESOLUTION_FAILED':
        // Fall back to asking for a full address
        break;
      case 'IP_RESOLUTION_INSUFFICIENT_US':
        // Ask for ZIP code and state
        break;
      case 'IP_RESOLUTION_INSUFFICIENT_CA':
        // Ask for province
        break;
      case 'INVALID_IP_FORMAT':
        // Check IP address format
        break;
      case 'CERTIFICATE_NOT_FOUND':
      case 'CERTIFICATE_REQUEST_NOT_FOUND':
        // Resource doesn't exist or belongs to another account
        break;
      case 'REQUEST_ALREADY_FULFILLED':
        // Fetch the resulting certificate instead of canceling
        break;
      case 'REQUEST_INVALID':
        // The request is in a terminal invalid state — start over
        break;
      case 'DOCUMENT_URL_UNAUTHORIZED':
        // Presigned URL expired — mint a fresh one and resubmit
        break;
      case 'BUYER_IDENTITY_CONFLICT':
        // customer.id and customer.reference_customer_id disagree — send one
        break;
      default:
        console.error(`Error: ${error.message}`);
    }
  }
} catch (err) {
  // Handle network errors
}
```

### Best Practices

1. **Always check the `type` field** for programmatic error handling
2. **Display the `message` field** to users when appropriate
3. **Log the full error response** for debugging
4. **Handle specific error types** rather than just checking status codes
5. **Use `resolution: "best_effort"`** if you want to avoid IP resolution errors and prefer zero-rate fallbacks


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.