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

# API reference

> Review the trusted service and hosted-browser endpoints for Boxpressd Payments.

The production API base URL is `https://payments.boxpressd.com/api/v1`. Approved backends authenticate with both `Authorization: Bearer <service-client-secret>` and `X-Boxpressd-Client-Id`.

## Trusted service endpoints

| Method | Path | Purpose |
| - | - | - |
| `POST` | `/billing-accounts/resolve` | Resolve an assertion-authorized billing account and current balance. |
| `POST` | `/billing-accounts/auto-reload` | Configure assertion-authorized automatic credit reload. |
| `GET` | `/offers?resourceType={type}` | List active offers available to a registered resource type. |
| `POST` | `/payment-sessions` | Create a short-lived hosted session. |
| `GET` | `/payment-sessions/{id}/status` | Get authoritative session and purchase status. |
| `GET` | `/merchant-accounts` | List merchant configuration status for an authorized owner. |
| `POST` | `/merchant-accounts` | Create an authorized merchant account. |
| `PUT` | `/merchant-accounts/{id}/credentials` | Configure or rotate gateway credentials. |
| `DELETE` | `/merchant-accounts/{id}/credentials` | Revoke gateway credentials. |

Merchant routes require `merchant_accounts:read` or `merchant_accounts:write`. Responses expose configuration status but never return a gateway secret, encrypted credential, or vault reference.

## Hosted-browser endpoints

These routes require the signed, HttpOnly action cookie created when the browser redeems a one-time session:

| Method | Path | Purpose |
| - | - | - |
| `POST` | `/payment-sessions/redeem` | Redeem the one-time URL token. |
| `GET` | `/payment-sessions/current` | Load the active hosted session. |
| `POST` | `/payment-sessions/current/cancel` | Cancel the active session. |
| `GET`, `POST` | `/payment-methods` | List or add payment methods. |
| `PATCH` | `/payment-methods/{id}/default` | Make a method the default. |
| `DELETE` | `/payment-methods/{id}` | Remove a saved method. |
| `POST` | `/purchase-intents/{id}/confirm` | Confirm and capture the active purchase. |

Raw card data is accepted only by `POST /api/pci/v1/payment-methods` on the Payments origin. Caller backends and parent applications must not proxy or log that payload.

## Idempotency

`POST /payment-sessions` requires `Idempotency-Key`. The purchase confirmation and server-managed offer creation routes also require it. Use a stable key for one logical attempt, and generate a new key only for new work.

## Common errors

| Status | Error | Meaning |
| - | - | - |
| `400` | `idempotency_key_required` | A required idempotency key is missing or invalid. |
| `400` | `return_not_allowed` | The return or cancel URL is not registered. |
| `400` | `origin_not_allowed` | The iframe parent origin is not registered. |
| `401` | Authentication error | The service client or hosted action session is invalid. |
| `403` | `action_not_allowed` | The client policy does not allow the requested action. |
| `403` | `billing_account_assertion_required` | This client must prove account-specific authorization. |
| `404` | `merchant_account_not_found` | The merchant is not ready in this environment. |
| `409` | `merchant_not_ready` | The server-configured offer cannot use its merchant account yet. |
| `410` | `session_unavailable` | The one-time hosted session is invalid, expired, or already redeemed. |

Errors use a JSON object with `error` and `message` fields.

## Verify fulfillment callbacks

Payments signs the exact raw request body with HMAC-SHA256. The signature header uses `v1=<hex-digest>`.

```javascript theme={null}
import { createHmac, timingSafeEqual } from "node:crypto";

export function verifyPaymentsCallback({ rawBody, timestamp, signature }) {
  const sentAt = Number(timestamp);
  if (!Number.isFinite(sentAt) || Math.abs(Date.now() / 1000 - sentAt) > 300) {
    return false;
  }

  const expected = `v1=${createHmac(
    "sha256",
    process.env.BOXPRESSD_PAYMENTS_WEBHOOK_SECRET,
  )
    .update(`${timestamp}.${rawBody}`)
    .digest("hex")}`;

  const actualBytes = Buffer.from(signature);
  const expectedBytes = Buffer.from(expected);

  return (
    actualBytes.length === expectedBytes.length &&
    timingSafeEqual(actualBytes, expectedBytes)
  );
}
```

Read `X-Boxpressd-Timestamp` and `X-Boxpressd-Signature` before parsing the body. Reject stale timestamps, verify the signature against the raw bytes, and deduplicate the event `id` before applying fulfillment. Return a successful status only after the event has been recorded durably.
