Integrations and webhooks
This is the live control plane for the signed-in Merchant's own scope only. A White Label or Agent cannot supply a descendant scope_id to read or update it.
GET /v1/bo/integrations?scope_id=<merchant_uuid>
PUT /v1/bo/webhooks/<webhook_uuid>Read integration health
GET /v1/bo/integrations requires both settings:read and webhooks:read, plus the settings and webhooks contracts. It returns a no-store integrations_v1 projection.
The response deliberately omits full URLs, paths, secrets, private keys, raw payloads, and response bodies. It exposes only the effective host, whether a path exists, route fallback, delivery counts, and sender health.
| Sender status | Meaning |
|---|---|
healthy | scheduler, queue consumer, DNS/fetch policy, DB sender RPC, and signing pair are ready |
degraded | the sender is ready, but the delivery backlog crossed its threshold |
unavailable | delivery is fail-closed with a non-sensitive reason |
The closed reason catalogue is queue_binding_unavailable, scheduler_composition_unavailable, signing_key_mismatch, database_function_unavailable, dns_policy_unavailable, and delivery_backlog.
Preview is read-only
A theme preview can render sample/read data, but must never submit a live mutation or hold a write credential.
Create or replace an endpoint revision
PUT requires a same-origin cookie session, the merchant_owner role, webhooks:manage, and fresh configuration step-up bound to webhook.endpoint.upsert, the method and canonical path, body and request digests, Idempotency-Key, correlation ID, and If-Match.
PUT /v1/bo/webhooks/4f8d85f0-1f3a-4fc5-9888-24c70fc70a6a
Content-Type: application/json
Idempotency-Key: wh_cfg_01K3M7Y9RS5TQ0WH2E7XKJ4C6B
If-Match: "0"
X-Correlation-Id: cfg_01K3M7Y9RS5TQ0WH2E7XKJ4C6B
X-Step-Up-Token: <request-bound-token>{
"scope_id": "5f6dcff9-4463-4908-adce-5fc6bc3b4b25",
"label": "Production events",
"api_version": "2026-08-01",
"deposit_url": "https://hooks.merchant.co/paygate/deposits",
"withdrawal_url": null,
"account_url": null,
"enabled": true,
"reason_code": "MERCHANT_CONFIG_UPDATE"
}- Use
If-Match: "0"to create and the current ETag/version to update. Every successful change creates an immutable revision. withdrawal_url=nullandaccount_url=nullfall back todeposit_url.- An exact replay returns the original receipt; reusing a key with another body is rejected.
- Changing a URL or disabling an endpoint cancels every non-terminal binding for the prior revision and clears its old lease/retry immediately. A request already sent before the change commits may finish once at the destination, but it is never retried. Event bytes, attempt history, and terminal bindings remain immutable.
Accepted URL policy
The URL must use lowercase ASCII https://, a public DNS hostname, and either the default port or :443 (:443 is removed during canonicalisation). The path may be absent or contain ASCII pchar only.
The service rejects userinfo, IP literals, punycode/Unicode hosts, internal/reserved suffixes, every form of percent encoding, query strings, fragments, backslashes, controls/spaces, repeated //, and exact . or .. path segments. Saving performs no DNS lookup. Before delivery, the sender checks both public A and AAAA answers and rejects private, reserved, or mixed results.
Fixed delivery contract
- Events currently accepted by this sender:
deposit.settled,withdrawal.partial_fill,withdrawal.terminal. - The exact raw body bytes are signed with Ed25519; the keyset and readiness test vector must agree before delivery is enabled.
- Success is exactly
200,201,202, or204;410is permanent. - Redirects are never followed; the total timeout is 15 seconds.
- At most 11 attempts use these delays in seconds:
0, 10, 60, 300, 1800, 7200, 21600, 43200, 86400, 86400, 86400. - Events are retained for at least 30 days. The queue carries an opaque pointer only; the DB lease remains authoritative.
