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

# Error Handling

> Every PCI Booking error condition with its exact response text, why it happens, and how to resolve it. Grouped by the part of the API that raises it.

Almost every failed API call returns the same three-part shape: a numeric `code`, a fixed `message` for that code, and a `moreInfo` string describing the specific failure. The main exception is a failed authentication, which returns `401` with an empty body (see [Authentication and Permission Failures](#authentication-and-permission-failures)). This page lists the conditions you are most likely to hit, with the exact text each returns and what to do about it.

## How to read an error

The `code` alone is rarely enough to identify a problem. Several codes are deliberately broad: `-125` covers every kind of bad input, and `-1003` covers everything from a missing permission to a deleted token. Three pieces of information together identify a condition:

<Steps>
  <Step title="The code">
    Narrows the class of failure, and maps to the HTTP status. See the table below.
  </Step>

  <Step title="The moreInfo string">
    Names the specific failure. This is the most useful field and the one to search for on this page. Some conditions leave it empty, which is itself a clue.
  </Step>

  <Step title="The endpoint you called">
    The same code and message mean different things on different endpoints. `-1003` with HTTP `403` is a permission problem; with HTTP `401` on a token call it usually means the token is not accessible to you.
  </Step>
</Steps>

<Warning>
  Errors are JSON only when the request's `Accept` header asks for JSON. Otherwise they are XML, whatever the format of the request body. See [Response Format](#response-format). Parse both `code` and `moreInfo` - handling only the HTTP status loses the detail that identifies the cause.
</Warning>

## Codes and their fixed text

The `message` for a given code never varies. Only `moreInfo` changes. The one exception is the `403` permission response, where the two fields are swapped (see [Authentication and Permission Failures](#authentication-and-permission-failures)).

| Code | `message` | HTTP |
| - | - | - |
| `-112` | Destination is bad | `400` |
| `-113` | This operation is not allowed for the given entity | `403` |
| `-123` | Message badly formatted | `400` |
| `-125` | Bad input data | `400` |
| `-126` | Request entity too large | `413` |
| `-150` | System error | `500` |
| `-160` | Uri not found | `404` |
| `-168` | Data integrity failed | `409` |
| `-175` | Request timed out | `504` |
| `-179` | Bad input parameter | `400` |
| `-1003` | You are not authorized to access this resource. Please contact customer support. | `401` or `403` |
| `-1010` | Resource is locked for updates | `423` |
| `0` | Application error: 0 | `500` |

<Note>
  A handful of codes exist in the platform but are never returned by this API - they belong to the portal's own sign-in flow. If you are building against the API you will not see `-174`, `-176`, `-180`, `-1004`, `-1005` or `-1006`. You will not see `-1002` (Missing authentication header) either: a missing `Authorization` header returns `401` with an empty body.
</Note>

## Response Format

Errors in PCI Booking's standard format (`code`, `message`, `moreInfo`) are JSON only when the request asks for JSON in its `Accept` header. In every other case they are XML. The format of the request body does not change this.

| `Accept` header of the request | Error body |
| - | - |
| `application/json` or `text/json` | JSON, with `Content-Type: text/json` |
| `application/xml`, `text/xml`, `text/html`, `*/*`, any other value, or no `Accept` header | XML, with `Content-Type: text/xml` |

Many HTTP clients, including curl, send `*/*` or no `Accept` header by default, so without the header you receive XML errors. A browser sends `text/html` first, so an error shown in an iframe is XML too.

Two kinds of error do not use this format and follow standard content negotiation instead, which returns JSON unless you ask for something else:

* The standard `400` validation response returned by some endpoints, for example the Payments Library, [Card Display with OTP](/api-reference/process-cards/card-display-otp) and payment gateway credentials endpoints. It lists the invalid fields in an `errors` object.
* Plain text messages, such as `Session token is invalid` on the Payments Library session endpoints.

The same standard error in the two formats:

```json JSON theme={null}
{
  "code": -125,
  "message": "Bad input data",
  "moreInfo": "Invalid card number",
  "errorList": null
}
```

```xml XML theme={null}
<ErrorBlock xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><code>-125</code><message>Bad input data</message><moreInfo>Invalid card number</moreInfo></ErrorBlock>
```

In XML, a field with no value is left out instead of being sent as `null`.

<Tip>
  Send exactly `Accept: application/json` on every server-to-server request, as the only value. PCI Booking splits the header at each comma, does not remove spaces, and matches each value exactly, including upper and lower case. It uses the first value it recognises. So `text/html, application/json` gives XML, and `Application/JSON` or `application/json; charset=utf-8` are not recognised. Note also that the JSON content type is `text/json`, not `application/json`.
</Tip>

## Authentication and Permission Failures

PCI Booking checks each request in two steps. First it identifies the caller from the API key, session token or access token (authentication). Then it checks that this caller is allowed to use the endpoint (permissions). The two steps fail in different ways:

| Failure | HTTP | Body |
| - | - | - |
| Authentication | `401` | Empty. There is no `code`, `message` or `moreInfo`. |
| Permissions | `403` | Code `-1003`, with the reason in `message`. |

### Authentication failed: 401 with an empty body

The response is the same for every authentication cause, so the body cannot tell you which one applies. Check each cause in the entry below.

<AccordionGroup>
  <Accordion title="401 with an empty body - authentication failed">
    **HTTP status:** `401`

    **Response body:** empty. There is no `code`, `message` or `moreInfo`.

    **Reason.** PCI Booking could not identify the caller. The response is the same for every cause:

    * The `Authorization` header (or the `x-pcibooking-api-key` header) is missing, badly formatted, or has no `APIKEY` keyword.
    * The API key is not valid, or it belongs to the other environment (sandbox or production).
    * The `sessionToken` is not valid or has expired.
    * The `accessToken` is not valid, has expired, has an expiration time further ahead than the maximum allowed, or was already used. An access token works once only, so reloading a page that uses it, such as a card display iframe, also returns this response.

    **How to resolve.**

    1. Confirm the `APIKEY` prefix is present and there is a single space between it and the key.
    2. Confirm the key belongs to the same environment as the host you called. Sandbox and production keys are not interchangeable.
    3. If you use a session token, check it has not expired and start a new session if it has.
    4. If you use an access token, generate a new one for every request, including every reload of an iframe.
    5. Check that your HTTP client does not drop the `Authorization` header on a redirect. Most clients remove it after a 301 or 302.

    **See also:** [Authentication](/getting-started/authentication), [Authentication and Permission Failures](/reference/error-handling#authentication-and-permission-failures)
  </Accordion>
</AccordionGroup>

<Note>
  A `401` that does have a body, with code `-1003`, is not an authentication failure, except on the [Payments Library session endpoints](#payments-library-session-endpoints). PCI Booking identified the caller, but the caller cannot use the token or the sender in the request, or the account is not set up for CVV retention (`User is not authorized to handle bank cards`). See [Token and sender access](#token-and-sender-access) and [Token Not Found or Not Accessible](#token-not-found-or-not-accessible).
</Note>

### Permission denied: 403 with code -1003

When PCI Booking identifies the caller but the caller is not allowed to use the endpoint, the response is `403` with code `-1003`. In this response the two text fields are the other way round compared with other errors: `message` holds the specific reason, and `moreInfo` holds the fixed `-1003` text.

```json 403 theme={null}
{
  "code": -1003,
  "message": "User permissions <permissions> not allowed for this action",
  "moreInfo": "You are not authorized to access this resource. Please contact customer support.",
  "errorList": null
}
```

| `message` | Reason | How to resolve |
| - | - | - |
| `User permissions <permissions> not allowed for this action` | Your user does not have a permission that this endpoint needs. `<permissions>` shows the permissions your user has. | Ask your account administrator to give your user the permission, or call the endpoint with a user that has it. |
| `User type <type> not allowed for this operation` | The endpoint is limited to one type of account. For example, managing payment gateway credentials requires a `Booker` account. | Call the endpoint with a user of the right type. |
| `User from IP Address [<ip>] is not allowed to access the service` | Your account has an allowed IP list, and the request came from an address that is not on it. | Add the address to the list, or send the request from an allowed address. |
| empty (`null`) | Your account is blocked because it has exceeded its processing allowance. | Contact [support](https://pcibooking.net/pci-booking-support/) to have the allowance reviewed. |

### Payments Library session endpoints

The Payments Library endpoints under `/api/eWalletOperation` that take a session in an `Authorization: Bearer <session token>` header behave differently. If that header is missing or is not in the `Bearer` form, they return `401` with a body:

```json 401 theme={null}
{
  "code": -1003,
  "message": "You are not authorized to access this resource. Please contact customer support.",
  "moreInfo": "Authorization header not found or invalid",
  "errorList": null
}
```

If the header is present and in the `Bearer` form but the session token is not valid, they return `401` with the plain text body `Session token is invalid`.

## Token and sender access

<AccordionGroup>
  <Accordion title="-1003 You are not authorized to access this resource - token does not exist, was deleted, or is not yours">
    **HTTP status:** `401`

    **`message`:** `You are not authorized to access this resource. Please contact customer support.`

    **`moreInfo`:** one of the following, depending on which check failed:

    ```text theme={null}
    User is not the owner of this bank card
    Merchant or Owner are not associated with bank card [<token>] userID: <userId>
    User <userId> is not associated with bank card [<token>]
    User <userId> cannot delegate token <token> - not owner and lacks delegation rights
    User is not authorized to handle bank cards
    ```

    **Reason.** The credential authenticated fine, but it cannot act on the token in the request. Either the token does not exist or it is not yours to use. The generic wording makes this the single most misread error in the API. On a token call it is far more often one of the causes below than an actual permissions problem.

    **How to resolve.**

    1. **Check the token still exists.** This is the most common cause. On every token endpoint except Delete Token, a token that does not exist or was deleted returns this error, the same as a token that belongs to another account. See [Token Not Found or Not Accessible](/reference/error-handling#token-not-found-or-not-accessible).
    2. **Check the tokenization actually succeeded.** If the call that should have created the token failed, the token never existed, and calls that use it report `-1003`.
    3. **Check the environment.** A sandbox token cannot be used from production, or the reverse.
    4. **Check ownership and association.** See the rules below.

    **Ownership and association.**

    A token is owned by the account that created it. Another account can use the token only if the token was explicitly associated with it:

    * **At tokenization**, by passing `merchantId` on the tokenizing call.
    * **After tokenization**, by associating the token with the merchant.

    Two rules catch people out:

    * An association can only target a **primary** account. If you pass the ID of a sub-user or a secondary property, the request is rejected and you must associate the token with the parent account instead.
    * Tokens never cross environments. A token created in sandbox cannot be used from production, and the reverse is also true.

    **Account level blocks.**

    An account that has exceeded its processing allowance is blocked, and every billable API call from it then fails with HTTP `403` and code `-1003`, even though the credentials are still valid and can still sign in to the portal. In this response `message` is empty (`null`) and `moreInfo` holds only the generic `-1003` text, so nothing in the body explains the block. The `403` status is what tells it apart from a token that is not accessible, which returns `401`.

    This is a common cause on sandbox accounts, which have a lower allowance than production.

    **Check for it when a `403` with `-1003` appears suddenly across calls that used to work, on more than one token.** A block affects every billable call on the account at once, whereas a genuine ownership problem affects only the specific token. Contact [support](https://pcibooking.net/pci-booking-support/) with your account name to have the allowance reviewed and the block lifted. The block stays until support lifts it.
  </Accordion>

  <Accordion title="-1003 You are not authorized to access this resource - SenderID does not match the credential">
    **HTTP status:** `401`

    **`message`:** `You are not authorized to access this resource. Please contact customer support.`

    **`moreInfo`:**

    ```text theme={null}
    Wrong sender id
    ```

    **Reason.** The `SenderID` in the body does not match the account the API key authenticates as. The API will not let one account send a card request on behalf of another.

    **How to resolve.**

    1. Set `SenderID` to the user ID of the account whose API key you are using.
    2. If you operate several accounts, check you have not paired one account's key with another account's `SenderID`.
  </Accordion>
</AccordionGroup>

## Token Not Found or Not Accessible

PCI Booking does not tell you whether a token exists unless you are allowed to use it. This is a security measure: nobody can use the API to find out whether a token exists.

On every token endpoint except [Delete Token](/api-reference/manage-tokens/delete-token), these three cases return the same response, HTTP `401` with code `-1003`:

* The token never existed.
* The token was deleted.
* The token belongs to another account and is not associated with yours.

The `moreInfo` text depends on the endpoint and on which access check ran. It never tells you which of the three cases applies. The possible texts are listed in the `-1003` entry under [Token and sender access](#token-and-sender-access).

### Delete Token is the exception

Delete Token checks whether the token exists before it checks who owns it, so it tells the cases apart:

| Case | HTTP | `code` | `moreInfo` |
| - | - | - | - |
| The token does not exist or was already deleted | `404` | `-160` | `The provided card token does not exist or was already deleted` |
| The token exists but belongs to another account | `401` | `-1003` | `User is not the owner of this bank card` |

It is safe to send the same DELETE request again. If the token is already gone, nothing changes and you get `404` / `-160`.

### Malformed tokens

A token is 32 hexadecimal characters. If the value you send is not in that format, the response depends on where the token is in the request:

* **Token in the URL path** (for example `/api/payments/paycard/{cardToken}/meta`): HTTP `404` with an empty body and no PCI Booking error code. The request did not match any endpoint.
* **Token in a query parameter, and the card display form**: HTTP `401` with code `-1003`, the same as a token that does not exist. This applies to `cardToken` on [Token Replacement](/api-reference/process-cards/token-replacement), `cardUri` on [Duplicate Token](/api-reference/manage-tokens/duplicate-token) and [Network Tokenize from Token](/api-reference/additional/network-tokenize-from-token), and the path token on [Request Card Display Form](/api-reference/process-cards/request-card-display-form).

### How to confirm that a token was deleted

Use any of these:

1. **The delete response.** A `200` from Delete Token means the token was deleted.
2. **Send the DELETE again.** A `404` with code `-160` confirms the token no longer exists. A `401` with code `-1003` means the token exists but is not yours.
3. **Query by reference.** If you gave the token a unique reference, call [Query Tokens](/api-reference/manage-tokens/query-tokens) with that reference. A `200` with an empty array means no token that you own has that reference. Only tokens your account owns are searched.
4. **Ask support.** Send the token to [support@pcibooking.net](mailto:support@pcibooking.net) and we will check it for you.

## Tokens

<AccordionGroup>
  <Accordion title="-160 Uri not found - wrong operation for this kind of record">
    **HTTP status:** `404`

    **`message`:** `Uri not found`

    **`moreInfo`:**

    ```text theme={null}
    Resource not matching request
    Resource not matching request. Cannot update a paycard
    Resource not matching request. Cannot retrieve a paycard
    ```

    **Reason.** The token exists, but it does not hold the kind of record this endpoint works on. The usual case is calling a card operation against a token that holds payment information rather than a card, or calling a card entry operation against a token created by a different capture type.

    **How to resolve.**

    1. Check which endpoint created the token, and use the matching endpoint to read or update it.
    2. For a card captured through a hosted form, use the retrieval endpoint for that capture type.
  </Accordion>

  <Accordion title="-125 Bad input data - expiration date rejected">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:**

    ```text theme={null}
    Expiration year/month error
    ```

    **Reason.** The expiration month or year is missing, out of range, or in a format the endpoint does not accept.

    **How to resolve.**

    1. Send the month as two digits, `01` through `12`.
    2. Check the year format the endpoint expects. Some accept two digits and some four, and they are not interchangeable.
    3. Check the date is not in the past. An expired card is rejected at tokenization.

    **See also:** [Card Validation Errors](/reference/card-validation-errors)
  </Accordion>

  <Accordion title="-179 Bad input parameter - CVV format rejected">
    **HTTP status:** `400`

    **`message`:** `Bad input parameter`

    **`moreInfo`:**

    ```text theme={null}
    Invalid CVV value: <value>
    ```

    **Reason.** The CVV was present but did not match three or four digits. The API applies the same rule to every card brand.

    **How to resolve.**

    1. Send three or four digits, digits only, with no spaces or punctuation. The API accepts either length for any brand - it does not require four digits for American Express, and it does not reject four digits on other brands.
    2. Omit the field entirely rather than sending an empty string. An absent or empty CVV is accepted, and the card is simply stored without one.

    <Note>
      The [hosted card entry form](/capture-cards/hosted-card-entry-form) is stricter than the API. Where it has detected the brand it accepts exactly three digits for every brand except American Express, which takes three or four; where the brand is unknown it accepts three or four. A CVV that the form rejects would therefore have been accepted had you sent it to the API directly.
    </Note>
  </Accordion>
</AccordionGroup>

## Accounts and merchant association

<AccordionGroup>
  <Accordion title="-125 Bad input data - merchant not found for this account">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:**

    ```text theme={null}
    Could not find a valid merchant <merchantId> for <userId>
    ```

    **Reason.** The `merchantId` you passed is not a merchant that your account can associate a token with. Either the ID does not exist, or it belongs to an account unrelated to yours.

    **How to resolve.**

    1. Check the `merchantId` value. For a property it is the external user ID, not the internal one.
    2. Confirm the merchant sits under the same parent account as the credential making the call.
  </Accordion>

  <Accordion title="-113 This operation is not allowed for the given entity - association must target the primary account">
    **HTTP status:** `403`

    **`message`:** `This operation is not allowed for the given entity`

    **`moreInfo`:**

    ```text theme={null}
    Please associate the card to the primary property
    Please associate the card to the primary booker - <accountId>
    ```

    **Reason.** A token can only be associated with a **primary** account. You passed the ID of a sub-user or a secondary property, which cannot own an association.

    **How to resolve.**

    1. Use the primary account's ID instead. When the message names an account ID, that is the one to use.
    2. Sub-users of the primary account can then use the token without needing their own association.
  </Accordion>
</AccordionGroup>

## Token replacement and relay

<AccordionGroup>
  <Accordion title="-125 Bad input data - profile not found">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:** the wording depends on which endpoint you called:

    ```text theme={null}
    Couldn't fetch a valid screening profile:: <profileName>
    Couldn't fetch a valid pciShield profile:: <profileName>
    ```

    The first is returned by token replacement in a request (`paycard/relay`). The second is returned by tokenization on response (`paycard/capture`). They mean the same thing.

    **Reason.** The `profileName` you passed could not be resolved for the account making the request. In almost every case the profile does exist, but on a different account than the one your credentials belong to. A misspelled profile name produces the same error.

    **How to resolve.**

    A PCI Shield profile belongs to the **account**, not to the sub-user that calls the API. When a sub-user makes a request, the profile is looked up against that sub-user's parent account. A profile configured on one account is never visible to a sub-user of a different account, and sandbox and production accounts are separate.

    **To check which profiles your credential can see:**

    1. Identify the parent account of the sub-user whose credentials you are using.
    2. Sign in to the [PCI Booking portal](https://users.pcibooking.net) as that account and open **PCI Shield Settings** > **PCI Shield Profile Settings**.
    3. Confirm the profile name appears there, spelled exactly as you send it. Profile names are matched exactly.
    4. If the profile is listed under a different account, switch your request to a sub-user of that account rather than copying the profile.

    <Note>
      There is no API endpoint that lists the profiles on your own account. `GET /api/booker` returns the platform-wide preset tokenization profiles, which are a different set. Your own profiles are visible in the portal only.
    </Note>

    **See also:** [Content Filters](/account-setup/content-filters), [Target Profiles](/account-setup/target-profiles)
  </Accordion>

  <Accordion title="-125 Bad input data - card data could not be substituted">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:**

    ```text theme={null}
    Replacement of content failed
    Could not apply token replacement onto content
    ```

    **Reason.** The body parsed, but the profile's selectors did not match anything in it, so no card data was substituted. The request was not relayed. This is a mismatch between the profile and the payload, not a problem with the token.

    **How to resolve.**

    1. Compare the profile's selectors against the exact payload you sent. A selector that assumes a different nesting depth or element name matches nothing.
    2. Check the namespaces. For XML and SOAP, a selector written without namespace handling will not match a namespaced document.
    3. Confirm the body is the format the profile was written for. A profile written for XML will not match a JSON body.
    4. Test the profile against a saved copy of a real request before using it in production.

    **See also:** [Content Filters](/account-setup/content-filters)
  </Accordion>

  <Accordion title="-125 Bad input data - request body is empty">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:**

    ```text theme={null}
    Content is empty
    Content is missing/empty
    Empty relay message content
    ```

    **Reason.** Token replacement had nothing to work on. The body of the request you asked to be relayed was empty, so there was no content in which to substitute card data.

    **How to resolve.**

    1. Send the third-party request you want relayed as the body of the call, not as a query parameter.
    2. If you are pointing at the content with a parameter name, check that parameter is present and actually carries the payload.
    3. Check no proxy or client library between you and the API is dropping the body on a `GET` relay.
  </Accordion>

  <Accordion title="-125 Bad input data - Content-Type header missing">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:**

    ```text theme={null}
    Content type is missing/empty
    ```

    **Reason.** Token replacement needs to know how to parse the body before it can find the placeholders. Without a `Content-Type` header it cannot choose a parser.

    **How to resolve.**

    1. Set `Content-Type` to match the body you are sending, for example `application/json`, `text/xml`, or `application/x-www-form-urlencoded`.
    2. Send the charset if the third party requires one, for example `text/xml; charset=UTF-8`.
  </Accordion>

  <Accordion title="-125 Bad input data - unsupported httpMethod value">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:**

    ```text theme={null}
    httpMethod should be 'POST','GET','PUT','PATCH','DELETE'
    ```

    **Reason.** The `httpMethod` parameter names a method the relay does not forward.

    **How to resolve.**

    1. Use one of `POST`, `GET`, `PUT`, `PATCH` or `DELETE`, in upper case.
    2. Omit the parameter to accept the default of `POST`.
  </Accordion>

  <Accordion title="-125 Bad input data - targetUri not usable">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:**

    ```text theme={null}
    Target Uri invalid
    ```

    **Reason.** The `targetUri` could not be parsed as an absolute URL, so the relay had nowhere to send the request.

    **How to resolve.**

    1. Send an absolute URL including the scheme, for example `https://api.example.com/path`.
    2. URL-encode the value if you pass it as a query parameter, so that its own query string does not terminate yours.
  </Accordion>

  <Accordion title="-125 Bad input data - no token found in the custom header">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:**

    ```text theme={null}
    Token(s) could not be found in custom header X-pciBooking-cardUri
    ```

    **Reason.** The relay was told to take the token from the `X-pciBooking-cardUri` header, but the header was absent or held no readable token.

    **How to resolve.**

    1. Add the `X-pciBooking-cardUri` header carrying the full token URI.
    2. Separate multiple tokens as the endpoint documents, and check none of them is empty.
  </Accordion>

  <Accordion title="-175 Request timed out - third party did not respond in time">
    **HTTP status:** `504`

    **`message`:** `Request timed out`

    **`moreInfo`:** not populated for this condition. The code and the endpoint are the only signal.

    **Reason.** PCI Booking relayed your request but the target server did not answer within the timeout. The failure is on the far side, not in PCI Booking. Card data may already have reached the third party, so the operation cannot be assumed not to have happened.

    **How to resolve.**

    1. Check how long the third party takes to respond. The complete round trip must finish within 29 seconds, so raising `timeout` beyond that does not help. If the third party regularly needs longer, contact [support@pcibooking.net](mailto:support@pcibooking.net) for an alternative setup.
    2. Confirm the target host is reachable and not rate limiting or blocking the call. PCI Booking calls the third party from its own addresses, which the third party may need to allow.
    3. **Do not blind retry a charge.** Query the third party for the outcome first. A timeout is an unknown result, not a failure.

    **See also:** [Outbound IPs](/reference/outbound-ips)
  </Accordion>
</AccordionGroup>

## Request format

<AccordionGroup>
  <Accordion title="-123 Message badly formatted - body is not valid XML for this endpoint">
    **HTTP status:** `400`

    **`message`:** `Message badly formatted`

    **`moreInfo`:**

    ```text theme={null}
    Bad XML document
    Bad XML document. Errors: <validation errors>
    ```

    **Reason.** The endpoint expects XML that validates against its schema, and the body either did not parse as XML or failed validation. When validation is the cause, the failing details are listed in `moreInfo`.

    **How to resolve.**

    1. Read the list in `moreInfo`. It names the elements that failed, which is usually enough on its own.
    2. Check element order. The schema is sequence-sensitive, so correctly named elements in the wrong order still fail.
    3. Check the declared encoding matches what you actually sent.

    **See also:** [Card Data XML Structure](/reference/card-data-xml-structure)
  </Accordion>
</AccordionGroup>

## Payment gateway

<AccordionGroup>
  <Accordion title="-125 Bad input data - request object could not be parsed">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:**

    ```text theme={null}
    Request object could not be parsed
    ```

    **Reason.** The body could not be deserialised into the object the endpoint expects, so no field level validation ran. A single malformed field or a wrong container type is enough.

    **How to resolve.**

    1. Validate the body against the endpoint's schema before sending.
    2. Check for a value sent as the wrong type, most often a number sent as a string or an object sent where an array is expected.
    3. Check `Content-Type` matches the body format.
  </Accordion>

  <Accordion title="-125 Bad input data - card or token missing from the request">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:**

    ```text theme={null}
    Missing Card object
    Missing CardToken property
    Bad or Missing CardToken property
    ```

    **Reason.** The transaction carried no card to charge. Either the card object is absent, or the `CardToken` property is missing or not a readable token URI.

    **How to resolve.**

    1. Send exactly one of a card object or a `CardToken`, as the endpoint requires.
    2. Pass the full token URI, not the bare 32-hex token, unless the endpoint documents otherwise.
    3. Check the property name and its capitalisation against the endpoint reference.
  </Accordion>

  <Accordion title="-125 Bad input data - required transaction field missing">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:**

    ```text theme={null}
    Missing amount property
    Missing GatewayReference property
    ```

    **Reason.** A field the operation cannot run without was absent. Which field is required depends on the operation: a capture or refund needs the `GatewayReference` of the original authorisation, and an amount is required wherever money moves.

    **How to resolve.**

    1. Add the field named in `moreInfo`.
    2. For a capture, refund or void, send the `GatewayReference` returned by the original transaction rather than your own reference.
    3. Send the amount in the units the gateway expects, and check whether it takes minor units.
  </Accordion>

  <Accordion title="-125 Bad input data - client certificate unavailable">
    **HTTP status:** `400`

    **`message`:** `Bad input data`

    **`moreInfo`:**

    ```text theme={null}
    Cannot obtain client certificate
    ```

    **Reason.** The profile or gateway configuration names a client certificate for mutual TLS, but it could not be loaded. This is a configuration problem on the account, not a fault in your request.

    **How to resolve.**

    1. Check the certificate is uploaded against the account and the profile names it exactly.
    2. Check the certificate has not expired.
    3. Contact [support](https://pcibooking.net/pci-booking-support/) with the profile name if both look correct. Certificate installation is done on the PCI Booking side.
  </Accordion>
</AccordionGroup>

## Card requests

<AccordionGroup>
  <Accordion title="-160 Uri not found - card request not found">
    **HTTP status:** `404`

    **`message`:** `Uri not found`

    **`moreInfo`:**

    ```text theme={null}
    Card request not found
    Card request with the specified requestID was not found
    ```

    **Reason.** No card request exists for the `requestID` given. Card requests are not permanent: once completed or expired, a request is no longer retrievable.

    **How to resolve.**

    1. Check the `requestID` is the one returned when the request was created.
    2. Check the request has not already been completed by the cardholder, or passed its expiry.
    3. Confirm the credential belongs to the account that created the request.
  </Accordion>
</AccordionGroup>

## When to contact support

Some conditions cannot be resolved from your side. Contact [support](https://pcibooking.net/pci-booking-support/) when:

* A `-1003` persists after you have ruled out deletion, environment and association. Support can tell you whether a token was deleted, and by which user. To check a deletion yourself first, see [How to confirm that a token was deleted](#how-to-confirm-that-a-token-was-deleted).
* Calls that used to work start failing with `403` and `-1003` across the whole account at once, which suggests a processing allowance block.
* A `-150` repeats on the same well-formed request.
* A client certificate is named correctly but cannot be loaded.

Include the `code`, `message` and `moreInfo` verbatim, the endpoint and HTTP method, the token URI or your own reference, and whether the call was against sandbox or production.

## Related

* [Return Codes](/reference/return-codes) - flat lookup table of every code
* [Card Validation Errors](/reference/card-validation-errors) - card number, expiration and CVV validation detail
* [Troubleshooting](/reference/troubleshooting) - symptom-first guide when you do not have an error code
* [API Conventions](/reference/api-conventions) - request and response formats


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