# Issue a member token

`POST https://app.booksbyinstant.com/api/integrator/v1/customers/{id}/members/{memberId}/tokens`

Not idempotent: each call issues a new token. The secret is in this answer only. A token whose answer was lost is never shown again and counts toward the 20 until it is revoked or expires: list the member's tokens and revoke what nobody holds.

operationId `issueMemberToken`; tag Member tokens.

## Example request

cURL:

```bash
curl -X POST 'https://app.booksbyinstant.com/api/integrator/v1/customers/01900000-0000-7000-8000-000000000000/members/01900000-0000-7000-8000-000000000000/tokens' \
  -H "Authorization: Bearer $INTEGRATOR_CREDENTIAL" \
  -H "Content-Type: application/json" \
  -d '{
  "expiresAt": "2026-01-15T09:30:00.000Z"
}'
```

JavaScript:

```javascript
// Node 18 or later, in an ES module (.mjs), which allows the top-level await.
const res = await fetch("https://app.booksbyinstant.com/api/integrator/v1/customers/01900000-0000-7000-8000-000000000000/members/01900000-0000-7000-8000-000000000000/tokens", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.INTEGRATOR_CREDENTIAL}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "expiresAt": "2026-01-15T09:30:00.000Z"
  }),
});
const data = await res.json();
```

Python:

```python
import os
import requests

res = requests.post(
    "https://app.booksbyinstant.com/api/integrator/v1/customers/01900000-0000-7000-8000-000000000000/members/01900000-0000-7000-8000-000000000000/tokens",
    headers={
        "Authorization": f"Bearer {os.environ['INTEGRATOR_CREDENTIAL']}",
    },
    json={
        "expiresAt": "2026-01-15T09:30:00.000Z",
    },
)
data = res.json()
```

## Authorization

- Bearer token `live_<100 hex>`: `Authorization: Bearer <secret>`. A 401 answers `WWW-Authenticate: Bearer realm="instant-integrator-api"`.

## Path parameters

- `id` (string · uuid; required): The customer's id.
- `memberId` (string · uuid; required): The member's id.

## Request body

At most 4096 bytes.

`application/json`, required: object; no other fields

A JSON object, {} at least: a request without one is refused. A token is issued only to an active member, who has at most 20 active tokens.

- `expiresAt` (string · date-time): When the token stops working: an ISO 8601 date and time with its offset, such as 2027-01-31T12:00:00Z, in the future. Without one the token works until it is revoked.

Example, built from the schema:

```json
{
  "expiresAt": "2026-01-15T09:30:00.000Z"
}
```

## Responses

### 201

The token and its secret.

`application/json`: object

- `token` (object; required): The token just issued.
  - `id` (string · uuid; required): The token's id.
  - `hint` (string; required; matches `^[a-z]+_m_…[0-9a-f]{4}$`): The secret's prefix, … and its last four hex characters, to tell tokens apart.
  - `createdAt` (string · date-time; required): When the token was issued. ISO 8601 in UTC with milliseconds, such as 2026-11-06T10:00:00.000Z.
  - `expiresAt` (string · date-time or null; required): When the token stops working, or null when it works until it is revoked. ISO 8601 in UTC with milliseconds, such as 2026-11-06T10:00:00.000Z.
- `secret` (string; required; matches `^[a-z]+_m_[0-9a-f]{100}$`): The member token, shown this once. Keep it on your servers, in a secret manager.

Example, built from the schema:

```json
{
  "token": {
    "id": "01900000-0000-7000-8000-000000000000",
    "hint": "string",
    "createdAt": "2026-01-15T09:30:00.000Z",
    "expiresAt": "2026-01-15T09:30:00.000Z"
  },
  "secret": "string"
}
```

### 400

The body is not one JSON object, or a field is invalid; `error` says which. Also when `expiresAt` is not in the future.

`application/json`: object; `Error`

Every answer that is not a success.

- `error` (string; required): Instant's sentence for people. A 4xx keeps the refusal's own sentence; a 5xx always says "Instant is temporarily unavailable. Please try again shortly."
- `code` (string; required): Stable, and what a caller branches on: VALIDATION_ERROR (400), UNAUTHENTICATED (401), FORBIDDEN (403), NOT_FOUND (404), CONFLICT (409), LIMIT_EXCEEDED (413), UNSUPPORTED_MEDIA_TYPE (415), UNREADABLE_FILE (422), RATE_LIMITED (429) and UNAVAILABLE (5xx). More codes may be added; treat one you do not know by its status.
- `retryAfter` (integer; at least `1`): Seconds to wait before retrying, also sent as the `Retry-After` header. Present only when there is a wait, as on a 429.
- `existingId` (string · uuid): On a 409 that names the object already there, such as a customer with that reference: its id.

Example, built from the schema:

```json
{
  "error": "string",
  "code": "VALIDATION_ERROR",
  "retryAfter": 1,
  "existingId": "01900000-0000-7000-8000-000000000000"
}
```

### 401

The credential is missing, malformed, unknown, revoked or rotated, or its integrator is disabled. All of these answer the same way.

Headers:

- `WWW-Authenticate` (string): Bearer realm="instant-integrator-api"

`application/json`: the `Error` object, as in 400.

### 404

`id` or `memberId` name nothing this caller can see.

`application/json`: the `Error` object, as in 400.

### 409

The member is deactivated, or already has 20 active tokens: revoke one first.

`application/json`: the `Error` object, as in 400.

### 413

The body is over 4096 bytes.

`application/json`: the `Error` object, as in 400.

### 415

The body is not sent as `application/json`.

`application/json`: the `Error` object, as in 400.

### 5XX

Instant is temporarily unavailable. Retry later, with backoff.

`application/json`: the `Error` object, as in 400.

---

Page: https://booksbyinstant.com/docs/issue-member-token/ · Overview: https://booksbyinstant.com/docs/index.md
