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

# Server-to-server API

> Routes your backend calls with HMAC auth.

All routes under `/integrations/*` are HMAC-authenticated. Full
request/response schemas with try-it-out controls are in the
**[API Reference](/api-reference)** tab.

## Endpoints at a glance

| Method   | Path                                   | Purpose                                  |
| -------- | -------------------------------------- | ---------------------------------------- |
| `POST`   | `/integrations/users`                  | Idempotently provision or update a user. |
| `DELETE` | `/integrations/users/{externalUserId}` | Anonymise a user (soft delete).          |

## `POST /integrations/users`

Idempotently creates or updates a user keyed by `(partner_id, externalUserId)`.
If the user was previously anonymised, this call **revives** the row and
clears `anonymizedAt`.

PII fields that are **omitted** are left unchanged. Passing an explicit
`null` clears them.

**Headers**

* `x-partner-slug`
* `x-signature`
* `content-type: application/json`

**Body**

| Field            | Type           | Required | Notes                                            |
| ---------------- | -------------- | :------: | ------------------------------------------------ |
| `externalUserId` | string (1–255) |     ✓    | Your stable ID.                                  |
| `email`          | string \| null |          | Set to `null` to clear; omit to leave unchanged. |
| `displayName`    | string \| null |          | Up to 200 chars.                                 |
| `phone`          | string \| null |          | E.164 recommended.                               |
| `countryCode`    | string \| null |          | ISO 3166-1 alpha-2 (auto-uppercased).            |
| `locale`         | string \| null |          | BCP-47 (e.g. `en-SG`). Currently informational.  |

**Responses**

| HTTP | Body                                            | Meaning                                            |
| ---- | ----------------------------------------------- | -------------------------------------------------- |
| 200  | `{ userId, created: false }`                    | Existing user updated.                             |
| 201  | `{ userId, created: true }`                     | New user created.                                  |
| 400  | `{ error: "validation_failed", issues: [...] }` | Bad body.                                          |
| 401  | `{ error }`                                     | HMAC auth failed. See [errors](/reference/errors). |
| 403  | `{ error: "partner_disabled" }`                 | Your partner record is disabled.                   |
| 500  | `{ error: "upsert_failed" }`                    | Race-recovery failed; safe to retry.               |

## `DELETE /integrations/users/{externalUserId}`

Soft-deletes the user: clears email/displayName/phone/countryCode/locale and
stamps `anonymizedAt`. Redemption and claim history is **preserved** for
audit and reporting.

A subsequent `POST /integrations/users` with the same `externalUserId`
revives the row and re-populates the PII.

**Headers** — same as above. The body is empty; sign the empty string:
`hmac_sha256(secret, "${timestamp}.")`.

**Responses**

| HTTP      | Body                          | Meaning                                  |
| --------- | ----------------------------- | ---------------------------------------- |
| 204       | *(empty)*                     | Anonymised.                              |
| 401 / 403 | `{ error }`                   | HMAC / disabled.                         |
| 404       | `{ error: "user_not_found" }` | No user for `(partner, externalUserId)`. |
