Webhook Events & Integration
This page documents the public webhook consumption rules used by the public
/api payment surface.
Overview
Webhook delivery lets your system react to payment, escrow, and disbursement changes without aggressive polling.
Public Contract Notes
For the default public paymentAPI router:
webhook consumption is part of the public integration surface
endpoint registration and secret rotation are account settings, not routes in the public
/apicontractsigning secrets should be stored server-side and rotated from authenticated account tooling when needed
Private Management Contract
Webhook endpoint management is intentionally outside the public /api
payment contract. Piaxis dashboard and developer tooling use authenticated
account routes under /users:
POST /users/webhookscreates a merchant webhook endpoint.GET /users/merchant/webhookslists the authenticated merchant’s endpoints.PATCH /users/merchant/webhooks/{webhook_id}updates URL, description, events, activity state, or rotates the signing secret.POST /users/merchant/webhooks/{webhook_id}/testsends a live test event to the configured endpoint.DELETE /users/merchant/webhooks/{webhook_id}removes an endpoint.
Creation accepts a URL, description, and subscribed events. If no custom secret is supplied, Piaxis generates one; the full signing secret is returned only once, immediately after create or rotation. Store it server-side and rotate it from authenticated dashboard tooling when needed.
Merchant/developer webhook deliveries use the top-level event field instead.
Do not treat this dashboard-management envelope as the same payload shape used
by older public disbursement helper events.
Example test delivery envelope:
{
"event": "payment.succeeded",
"webhook_id": "0e4102fa-3119-42aa-8a9c-d92ef4fa83af",
"merchant_id": "7c74f7bd-9e57-4c49-a215-8480bb81a0c0",
"data": {
"status": "succeeded"
},
"timestamp": "2026-01-15T10:30:00+00:00"
}
Delivery Pattern
The public payment API posts JSON with this envelope:
{
"data": {
"event": "disbursement.status_updated",
"disbursement_id": "b6b93a8b-2c81-44bf-9d98-9a43f725b90f",
"status": "completed"
},
"timestamp": "2026-01-15T10:30:00+00:00"
}
Headers
Content-Type: application/json
User-Agent: piaxis-Webhook/1.0
X-piaxis-Signature: <hex_hmac_sha256_if_secret_is_configured>
X-piaxis-Event: <event_name>
The public helper uses these headers when a signing secret is configured.
Signature Verification
When a webhook secret is configured, Piaxis computes:
HMAC SHA256
over the raw JSON payload body
using the shared webhook secret
Python example
import hashlib
import hmac
def verify_signature(raw_body: bytes, header_value: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header_value)
Consumer Requirements
Your webhook endpoint should:
accept HTTP
POSTrequestsread the raw body before mutating it
verify
X-piaxis-Signaturewhen a secret is configureduse
X-piaxis-Eventordata.eventin the JSON body for routingtreat delivery as at-least-once and process idempotently
return a
2xxresponse after durable handling
Public Event Types
The public payment surface may deliver event names inside data.event. Event
names vary by subsystem. Examples in the current public surface include:
disbursement.status_updateddisbursement.cancelled
Future public events will remain documented in the public payment API reference.
Minimal Handler Example
from fastapi import FastAPI, Header, HTTPException, Request
app = FastAPI()
WEBHOOK_SECRET = "your_webhook_secret"
@app.post("/webhooks/piaxis")
async def handle_piaxis_webhook(
request: Request,
x_piaxis_signature: str | None = Header(default=None),
):
raw_body = await request.body()
if x_piaxis_signature:
if not verify_signature(raw_body, x_piaxis_signature, WEBHOOK_SECRET):
raise HTTPException(status_code=401, detail="Invalid webhook signature")
payload = await request.json()
event_name = payload.get("data", {}).get("event")
if event_name == "disbursement.status_updated":
# update your internal payment tracking here
pass
return {"ok": True}
Operational Guidance
Store webhook deliveries with your own request id or event id when present.
Make webhook handlers idempotent.
Reconcile with polling when a webhook is missed.
Never assume a richer payload than the current event requires.