# 

Everything here is current behavior, not a roadmap. Each one is either a gap
between the published description and what the gateway does, or a capability
integrators regularly expect and the API does not have.

## Capabilities the API does not have

|  |  |
|  --- | --- |
| **Webhooks** | No outbound notifications of any kind. Anything that happens after the response — settlement, an ACH return — is found by polling `GET /v1/transactions` |
| **Idempotency** | No `Idempotency-Key` header. Retrying a `POST /v1/transactions` that already succeeded creates a second transaction |
| **3-D Secure** | No field carries a 3DS result, ECI value, or cardholder authentication cryptogram |
| **Rate limiting** | No `429` is returned today, and no rate limit is published |
| **Delete** | No endpoint deletes anything. Deactivate an integration with `is_active: false` |
| **Users and roles** | Not modeled on the public API. A client and its API key are the unit of access |
| **Installments** | Not offered. Recurring payments are, and are a different thing |
| **Card Present** | The gateway is Card-Not-Present only. Both `entry_method` values are CNP |


## Validation the description does not state

These return `400` and the published schema gives no hint of them.

| Field | What is rejected |
|  --- | --- |
| `website` on `POST /v1/clients` | Anything over **30 characters**. No maximum is published |
| Any `email` | Plus-addressing, and any top-level domain longer than four characters. `ops+paradise@example.com` and `ops@acme.agency` both fail |
| `admin_contact_details.phone` | A separator after the country code — even though the shared `Phone` schema publishes `+1-800-555-1234` as an example. Send `+18005551234` |
| 52 string fields | An empty string, where the schema declares no `minLength` |


## Fields that do not mean what they look like

| Field | Behavior |
|  --- | --- |
| `payment_method.card_type` | **Always `UNKNOWN`** on a transaction response. The processor does not report it on the authorization. Read the transaction back from a reporting endpoint for the real value |
| `initiator` | Comes back **null** on `GET /v1/transactions/{id}`, whatever you sent. Trust the create response |
| `recurring_details.initial_network_transaction_id` | **Echoes the request.** On a subsequent recurring payment that sent only `initial_transaction_id`, it comes back null. The gateway still looked up the initial payment; it just does not report the value it found |
| `return_code`, `return_reason` | **Omitted entirely**, not null, when a transaction has not been returned. Code that reads the key without checking will throw on almost every transaction |
| `payment_method.payment_token` | **Never null** on the response to the transaction itself. A decline returns an empty string, and a failed tokenization returns the sentence `Payment method could not be tokenized.` A later read returns `null` for both. Check for a `CARD_TOKEN-`, `BANK_TOKEN-` or `PAYMENT_TOKEN-` prefix before storing it |
| `payment_method.expiry` | Null on capture, void and refund responses. Expiry is not echoed back on those |
| `payment_method.expiry.year` | **Two digits**, such as `"30"` for 2030, on the response to a transaction charged to a card token. The card payment that issued the token returns four, and so does a later read of either transaction. Read a two-digit year as 20YY |


## Report filters that mislead

A `200` is not proof a filter worked.

| Filter | Behavior |
|  --- | --- |
| `card_type=CREDIT` or `DEBIT` | Quietly drops most rows, because `card_type` is usually `UNKNOWN`. Include `UNKNOWN` unless you specifically want determined rows |
| `card_type` on `GET /v1/batches/transactions` | A **different parameter** from the one on `GET /v1/transactions`: narrower, no comma-separated list, and `UNKNOWN` returns `400` |
| `term` on `GET /v1/batches` | A piece the gateway does not recognize returns **`500` with `SYSTEM_ERROR`**, not `400`. Check each piece against [Batch reports](/docs/after-the-payment/batch-reports) before you send it |


## Inconsistencies across endpoints

Real, and not yet reconciled. Documented so you are not surprised.

**Paging and sorting differ by endpoint.**

| Endpoint | Page size | Skip | Sort |
|  --- | --- | --- | --- |
| `GET /v1/transactions` | `limit` | `offset` | `sort_by` |
| `GET /v1/batches` | `take` | `skip` | `sort_column` |
| `GET /v1/clients` | `Take` | `Skip` | `SortColumn` |


That last row is PascalCase, alone in an otherwise snake_case API.

**Processor names are spelled two ways.** `Tsys` and `Vericheck` in a request
body, `TSYS` and `VERICHECK` in a filter or a response. The body form is
case-sensitive and sending the wrong one returns `400`.

**Transaction type has three vocabularies.** `SALE`, `AUTH` and `PAYOUT` to
create; the response and filter vocabulary adds `INCREMENTAL_AUTH`, `REFUND`,
`CANCEL` and `CAPTURE`; and `VOID` or `REFUND` as the discriminator on a cancel
body. You send `VOID` and you read `CANCEL`.

**Batch status has three spellings.** `GET /v1/batches` reports `Opened`, `In-Progress`, `Closed` or `Failed`. `GET /v1/batches/status` reports the codes `"10"`, `"2"` and `"5"` as strings. The `term` filter takes `opened`, `in_progress`, `closed` and `failed`.

**Four transaction endpoints declare a `404` they never return.**

## Error codes the description does not declare

The description declares seven `error_code` values on its operations. Five more
can reach you: `INVALID_ACTION`, `REQUIRED_DATA_MISSING`,
`AMOUNT_LIMIT_EXCEEDED`, `VELOCITY_LIMIT_EXCEEDED` and
`INTEGRATION_CONFIG_ERROR`.

A generated SDK's error enum is therefore not the whole list. Handle an
unrecognized `error_code` by falling back on the HTTP status. See
[API response codes](/docs/reference/response-codes).

## Next steps

* [API response codes](/docs/reference/response-codes)
* [API changelog](/docs/reference/changelog)
* [Errors and retries](/docs/get-started/errors)