Separate credentials
Read, create, approve/sign, policy-admin, and workspace-admin capabilities belong in different keys and stores.
Concrete use: A reconciler key can list transactions but cannot create or approve one.
Failure mode: One omnipotent key turns a reporting host into a withdrawal host.
Signed request
Bind method, path, body hash, nonce/time, and short expiry to the caller identity.
Concrete use: Hash raw JSON bytes, issue JWT jti=req_8291, and reject clock skew outside the documented window.
Failure mode: Re-serializing JSON can change the signed bytes.
Idempotent create
Generate and persist the client external ID before the first network attempt.
Concrete use: Retry wd_8291 after timeout and query by the same ID before any second create.
Failure mode: A timeout has unknown outcome; blind retry can pay twice.
Raw-body verification
Verify webhook signature and timestamp over exact received bytes before parsing.
Concrete use: Queue the verified envelope, return 2xx quickly, then process asynchronously.
Failure mode: Signing parsed JSON breaks when whitespace/key order changes.
At-least-once consumer
Deduplicate event IDs, tolerate reordering, and compare polled authoritative state.
Concrete use: A late PENDING_SIGNATURE event cannot regress a locally reconciled COMPLETED transaction.
Failure mode: Webhook arrival order is not transaction order.
Silence alarm
No events can mean no activity or a broken integration; only polling distinguishes them.
Concrete use: Poll every 60 seconds and alert when webhook silence exceeds 5 minutes while state changes exist.
Failure mode: A green HTTP endpoint does not prove events are arriving.