Skip to content

Merchants API

PayWarden exposes two parallel surfaces for merchant management. Both require admin authentication (httpOnly admin_token cookie — see Authentication).

PrefixPurpose
/api/v1/admin/merchantsFull admin surface: list, create, detail with stats, update, rotate API key. Canonical. See Admin API.
/api/v1/merchantsLighter management surface used by the dashboard. Subset of the admin surface. Documented on this page.

Both surfaces return snake_case responses. Request bodies dual-accept snake_case (canonical) and camelCase (deprecated).

Merchant object

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "My Shop",
  "api_key_prefix": "pw_live_xxxx",
  "is_active": true,
  "created_at": "2026-01-01T00:00:00.000Z"
}

The full API key (api_key) is only returned at creation time and after key rotation — it is never readable again. The webhook_secret is write-only; it is used server-side to sign outgoing webhooks and is not echoed on list/detail reads.

Endpoints (/api/v1/merchants)

MethodPathDescription
POST/api/v1/merchantsCreate a merchant
GET/api/v1/merchantsList merchants
PUT/api/v1/merchants/:idUpdate name and/or is_active

For merchant detail (with stats), API key rotation, and webhook-secret updates, use the admin endpoints.

Create merchant

http
POST /api/v1/merchants
Cookie: admin_token=<jwt>
Content-Type: application/json

{
  "name": "My Shop",
  "webhook_secret": "a-long-random-string"
}
FieldTypeRequiredDescription
namestringyes1–255 chars
webhook_secretstringyesmin 10 chars. Used to HMAC-sign outgoing webhooks

Dual-accept: webhookSecret is also accepted (deprecated).

Response 201:

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "My Shop",
  "api_key": "pw_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "is_active": true,
  "created_at": "2026-01-01T00:00:00.000Z"
}

One-time display

api_key is returned only on creation. Store it immediately.

List merchants

http
GET /api/v1/merchants
Cookie: admin_token=<jwt>

Response 200:

json
{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "My Shop",
      "api_key_prefix": "pw_live_xxxx...",
      "is_active": true,
      "created_at": "2026-01-01T00:00:00.000Z"
    }
  ],
  "total": 1
}

Update merchant

http
PUT /api/v1/merchants/:id
Cookie: admin_token=<jwt>
Content-Type: application/json

{
  "name": "Renamed Shop",
  "is_active": false
}

Both fields optional; at least one must be present. Dual-accept: isActive also accepted.

Response 200:

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Renamed Shop",
  "is_active": false,
  "updated_at": "2026-04-08T12:34:56.000Z"
}

Errors:

  • 400 VALIDATION_ERROR — body failed schema validation, or no fields supplied
  • 404 NOT_FOUND — merchant id does not exist

Suspended merchants (is_active: false) cannot create new orders; existing orders continue processing.

Webhook secrets per merchant

Each merchant has a webhook_secret used to HMAC-sign webhook deliveries. PayWarden sets the X-Signature header on every webhook request:

X-Signature: sha256=<hmac-sha256(body, merchant.webhook_secret)>

See Webhooks for signature verification details.

Isolation in self-hosted mode

ResourceIsolated per merchant?
OrdersYes
Webhook events and secretsYes
HD wallet seedNo (single vault)
Address index poolNo (globally sequential)

For full per-merchant vault isolation, see PayWarden Cloud.

Released under the BSL 1.1 License.