# API conventions

The rules every endpoint follows, so the rest of the docs don't have to repeat them.

## Responses are wrapped

A successful call returns its result in `data`, next to three fields that describe the call:

```json
{
  "success": true,
  "data": { "id": "1c9f4a2e-…", "status": "OPEN" },
  "timestamp": "2026-07-16T14:02:10.000Z",
  "path": "/v1/payment-links"
}
```

Read `data`, and ignore fields you don't recognise: we add fields to `/v1` without notice.
Errors are not wrapped; they have [their own shape](https://developers.baxpay.com/errors).

## Lists and pagination

List endpoints take `page` (from 1) and `limit` (default 25, at most 100), and return the page in
`data.data` with the totals beside it:

```bash
curl "https://sandbox-api.baxpay.com/v1/payment-links?status=OPEN&page=1&limit=25" \
  -H "Authorization: Bearer $BAXPAY_API_KEY"
```

```js
// Walk every page of open payment links.
let page = 1;
const all = [];
for (;;) {
  const res = await fetch(`https://sandbox-api.baxpay.com/v1/payment-links?status=OPEN&page=${page}&limit=100`, {
    headers: { Authorization: `Bearer ${process.env.BAXPAY_API_KEY}` },
  });
  const { data } = await res.json();
  all.push(...data.data);
  if (page * data.limit >= data.total) break;
  page += 1;
}
console.log(JSON.stringify({ count: all.length }));
```

```json
{
  "success": true,
  "data": {
    "data": [{ "id": "1c9f4a2e-…", "status": "OPEN" }],
    "total": 1,
    "page": 1,
    "limit": 25
  }
}
```

A `limit` above 100 is treated as 100.

## Idempotency keys

Every call that creates something or moves money **requires** an `Idempotency-Key` header: any
unique string of 8 to 255 characters (a UUID is ideal). Without it the call is refused with `400`.

If a request times out, you don't know whether it went through. Send it again **with the same
key**: if the first attempt succeeded, you get its original response back and nothing is created
twice.

```bash
KEY=$(uuidgen)
BODY='{ "description": "Order #1042", "amount": 25, "businessWide": true }'

first=$(curl -s -X POST https://sandbox-api.baxpay.com/v1/payment-links \
  -H "Authorization: Bearer $BAXPAY_API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: $KEY" -d "$BODY" | jq -r .data.id)
again=$(curl -s -X POST https://sandbox-api.baxpay.com/v1/payment-links \
  -H "Authorization: Bearer $BAXPAY_API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: $KEY" -d "$BODY" | jq -r .data.id)

echo "first=$first again=$again"   # the same link: the retry created nothing
[ "$first" = "$again" ]
```

The rules:

| Situation | Result |
| --- | --- |
| Same key, same body, first call finished | The original response, replayed |
| Same key, same body, first call still running | `409`: wait a moment and retry with the same key |
| Same key, **different** body | `400`: a bug in your code, never retried for you |
| No key on a call that requires one | `400` |

Keys are remembered for **24 hours**, per merchant and per endpoint.

```bash
KEY=$(uuidgen)
curl -s -X POST https://sandbox-api.baxpay.com/v1/payment-links \
  -H "Authorization: Bearer $BAXPAY_API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: $KEY" -d '{ "description": "Order #1043", "amount": 25, "businessWide": true }' > /dev/null
curl -X POST https://sandbox-api.baxpay.com/v1/payment-links \
  -H "Authorization: Bearer $BAXPAY_API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: $KEY" -d '{ "description": "Order #1043", "amount": 30, "businessWide": true }'
```

```json
{
  "statusCode": 400,
  "message": "This Idempotency-Key was already used with a different request body.",
  "error": "Bad Request"
}
```

## Rate limits

Limits are counted **per IP address** over a rolling minute:

| Scope | Limit |
| --- | --- |
| Any endpoint (default) | 200 requests / minute |
| `POST /payment-links`, `POST /einvoices`, `POST /clients`, `POST /products` | 60 / minute |
| `POST /einvoices/{id}/send` | 30 / minute |

Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`.
Over the limit you get `429`: wait for the reset, then retry with the same idempotency key.

## Amounts

- **Payment links, e-invoices, clients and products** use decimal euros: `47.30`.
- **In-store sessions** (`/integration/…`) use integer minor units: `4730`, in fields ending in `Minor`.

Currencies are ISO 4217 codes in capitals (`EUR`).

## Ids, times and request ids

- Ids are UUIDs (`1c9f4a2e-6b1d-4c1e-9a52-0d9f6f2c1b7a`). Payment-link tokens (`plk_…`) are the only
  prefixed ids, and they belong in URLs you give to payers, never in API paths that need an id.
- Times are ISO 8601 in UTC (`2026-07-16T14:03:21.512Z`).
- Every call made with an API key gets a request id: the `X-Request-Id` response header, and
  `requestId` in an error. Send your own `X-Request-ID` (8 to 128 letters, digits, `.`, `_`, `:` or
  `-`) and it is used instead. Quote it when you contact support.

## Request log

Every call made with an API key is logged for **30 days**, in its mode: method, path (without the
query string), status, duration, request id, key and IP. Never a body, a header or a key. The
dashboard shows the same log under **Developers → Requests**.

- `GET /requests` — this mode's requests, newest first; filter by status, method, path, apiKeyId
- `GET /requests/{id}` — one request

`status` takes a class (`2xx`, `4xx`, `5xx`) and `path` any part of the path:

```bash
curl "https://sandbox-api.baxpay.com/v1/requests?status=2xx&path=/payment-links&limit=5" \
  -H "Authorization: Bearer $BAXPAY_API_KEY"
```

```json
{
  "success": true,
  "data": {
    "data": [
      {
        "id": "6e1d0f3a-…",
        "method": "POST",
        "path": "/v1/payment-links",
        "status": 201,
        "durationMs": 38,
        "requestId": "req_9b1c…",
        "ip": "203.0.113.9",
        "livemode": false,
        "apiKey": { "id": "0c5d…", "label": "Backend server", "prefix": "bx_test_", "last4": "a9f2" },
        "createdAt": "2026-07-16T14:03:21.512Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 5
  }
}
```

```bash
curl https://sandbox-api.baxpay.com/v1/requests/6e1d0f3a-… \
  -H "Authorization: Bearer $BAXPAY_API_KEY"
```

```json
{ "success": true, "data": { "id": "6e1d0f3a-…", "method": "POST", "status": 201, "requestId": "req_9b1c…" } }
```
