Merchants API
PayWarden exposes two parallel surfaces for merchant management. Both require admin authentication (httpOnly admin_token cookie — see Authentication).
| Prefix | Purpose |
|---|---|
/api/v1/admin/merchants | Full admin surface: list, create, detail with stats, update, rotate API key. Canonical. See Admin API. |
/api/v1/merchants | Lighter 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
{
"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)
| Method | Path | Description |
|---|---|---|
POST | /api/v1/merchants | Create a merchant |
GET | /api/v1/merchants | List merchants |
PUT | /api/v1/merchants/:id | Update name and/or is_active |
For merchant detail (with stats), API key rotation, and webhook-secret updates, use the admin endpoints.
Create merchant
POST /api/v1/merchants
Cookie: admin_token=<jwt>
Content-Type: application/json
{
"name": "My Shop",
"webhook_secret": "a-long-random-string"
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | 1–255 chars |
webhook_secret | string | yes | min 10 chars. Used to HMAC-sign outgoing webhooks |
Dual-accept: webhookSecret is also accepted (deprecated).
Response 201:
{
"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
GET /api/v1/merchants
Cookie: admin_token=<jwt>Response 200:
{
"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
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:
{
"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 supplied404 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
| Resource | Isolated per merchant? |
|---|---|
| Orders | Yes |
| Webhook events and secrets | Yes |
| HD wallet seed | No (single vault) |
| Address index pool | No (globally sequential) |
For full per-merchant vault isolation, see PayWarden Cloud.