Skip to content

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.

http
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 statusMeaning
healthyscheduler, queue consumer, DNS/fetch policy, DB sender RPC, and signing pair are ready
degradedthe sender is ready, but the delivery backlog crossed its threshold
unavailabledelivery 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.

http
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>
json
{
  "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=null and account_url=null fall back to deposit_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, or 204; 410 is 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.

เอกสารชุดนี้อิงสัญญา API เวอร์ชัน 2026-08-01