Skip to content

Webhooks (Endpoints)

Webhooks let Sangho notify your server in real time when important events occur — a successful payment, a refund issued, a subscription canceled, and so on. Rather than polling the API, your server receives an HTTP POST request as soon as the event occurs.

Each webhook endpoint listens to a configurable list of events and has its own HMAC-SHA256 signing secret. See the Events reference for the complete list of available types.

Always verify the signature

Every webhook request includes a Sangho-Signature header. Always verify this HMAC-SHA256 signature before processing the event — this guarantees that the request genuinely comes from Sangho and not from a malicious third party. See the signature verification guide.

Available events

EventTrigger
payment_intent.succeededPaymentIntent moved to succeeded status.
payment_intent.failedPaymentIntent moved to canceled status after failure.
transaction.succeededTransaction created and completed.
refund.succeededRefund returned to the buyer.
refund.failedRefund failed on the operator's side.
invoice.paidInvoice paid.
invoice.payment_overdueDue date passed without payment.
checkout.session.completedCheckoutSession moved to complete status.
subscription.createdNew subscription created.
subscription.canceledSubscription canceled.
subscription.payment_failedRenewal attempt failed.
customer.createdNew Customer created.
customer.deletedCustomer deleted.

Endpoints

MéthodeEndpointDescription
POST/webhooks/Create a webhook endpoint
GET/webhooks/List endpoints
GET/webhooks/{id}/Retrieve an endpoint
PATCH/webhooks/{id}/Update an endpoint
DELETE/webhooks/{id}/Delete an endpoint
POST/webhooks/{id}/disable/Disable (suspend deliveries)
POST/webhooks/{id}/enable/Re-enable a disabled endpoint
POST/webhooks/{id}/roll-secret/Regenerate the signing secret
POST/webhooks/{id}/test/Send a test event
GET/webhooks/{id}/deliveries/Delivery logs
POST/webhooks/{id}/deliveries/{did}/retry/Retry a failed delivery

Object schema

ResponseWebhook object
Full structure returned by all endpoints for this resource.
json
{
  "id": "wh_xxxxxxxxxxxx",
  "object": "webhook",
  "name": "Main production webhook",
  "url": "https://myapp.com/webhooks/sangho",
  "status": "active",
  "events": [
    "payment_intent.succeeded",
    "transaction.succeeded",
    "refund.succeeded",
    "subscription.canceled"
  ],
  "security_profile": "HMAC_SHA256",
  "ssl_verification": true,
  "retry_policy": {
    "max_attempts": 5,
    "backoff_type": "exponential"
  },
  "last_delivery_at": "2026-03-01T10:05:00Z",
  "success_rate": 0.987,
  "livemode": true,
  "created_at": "2026-01-01T00:00:00Z"
}
ResponseStructure of a received event
Body of the POST request sent to your endpoint for each event.
json
{
  "id": "evt_xxxxxxxxxxxx",
  "object": "event",
  "type": "payment_intent.succeeded",
  "created_at": "2026-03-01T10:05:00Z",
  "livemode": true,
  "data": {
    "object": {
      "id": "pi_3Nx8mLKZ2eZvKYlo28m",
      "object": "payment_intent",
      "amount": 15000,
      "currency": "XAF",
      "status": "succeeded",
      "customer": "cust_xxxxxxxx",
      "metadata": { "order_id": "CMD-2026-042" }
    }
  }
}

Create a webhook endpoint

Registers a new URL to which Sangho will send the selected events. The HMAC signing secret is returned only once at creation time — store it immediately in your secrets manager.

POSTRequest body
nameRequis
stringex :Production — orders

Descriptive name for the endpoint. Shown in your dashboard — use a name that clearly identifies the environment or purpose.

Max 255 characters. A good name includes the environment and functional scope, for example “Production — payments & subscriptions” or “Staging — all events”.

urlRequis

URL of your webhook endpoint. Must be publicly accessible over HTTPS. Sangho performs a validation ping at creation.

The URL must respond with a 2xx HTTP status code within 10 seconds to be validated. For local testing, use a tunnel such as ngrok or localtunnel.

eventsRequis
array[string]ex :[“payment_intent.succeeded”, “refund.succeeded”]

List of event types to listen for on this endpoint. Use [“*”] to receive all events.

Prefer an explicit list rather than “*” to reduce the volume of requests hitting your server and to simplify debugging. Each endpoint can listen to up to 50 distinct event types. See the events reference for the complete list.

security_profileOptionnel
stringdéfaut :HMAC_SHA256

Method used to sign webhook requests.

En savoir plus

Values: HMAC_SHA256 (recommended — Sangho-Signature header), JWT (signed JWT token in the Authorization header), BASIC (Basic Auth — not recommended in production). See the signature guide for implementation details.

ssl_verificationOptionnel
booleandéfaut :true

Enables SSL certificate verification for your endpoint.

En savoir plus

Only disable ssl_verification in a development environment with a self-signed certificate. In production, always keep this option set to true.

retry_policyOptionnel
object

Retry policy for delivery failures (timeout, 5xx error).

En savoir plus

Object with two fields: max_attempts (default: 5, max: 10) and backoff_type (exponential or linear, default: exponential). With exponential backoff, retries happen at 1 min, 5 min, 30 min, 2 h, 8 h. With linear: every 30 minutes.

metadataOptionnel
object

Free-form data associated with the endpoint — environment, owning team, etc.

Secret shown only once

The secret field returned at creation will never be shown again. Store it immediately in your secrets manager (environment variable, Vault, AWS Secrets Manager, etc.). If you lose it, generate a new one via POST /webhooks/{id}/roll-secret/.

Create an endpoint

bash
curl -X POST https://api.sangho.ga/v1/webhooks/ \
  -H "Authorization: Bearer sk_prod_xxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production webhook — orders",
    "url": "https://myapp.com/webhooks/sangho",
    "events": [
      "payment_intent.succeeded",
      "transaction.succeeded",
      "refund.succeeded",
      "subscription.canceled"
    ],
    "security_profile": "HMAC_SHA256",
    "retry_policy": {
      "max_attempts": 5,
      "backoff_type": "exponential"
    }
  }'
Response201 Created — Secret visible only once
The endpoint is created and active. The secret field is shown here and only here — store it immediately.
After this response, the secret is no longer accessible via the API. If you lose it, you'll need to roll the secret.
json
{
  "id": "wh_xxxxxxxxxxxx",
  "object": "webhook",
  "name": "Production webhook — orders",
  "url": "https://myapp.com/webhooks/sangho",
  "status": "active",
  "events": [
    "payment_intent.succeeded",
    "transaction.succeeded"
  ],
  "secret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "security_profile": "HMAC_SHA256",
  "livemode": true,
  "created_at": "2026-01-01T00:00:00Z"
}

Verify the signature

With every delivery, Sangho computes an HMAC-SHA256 signature of the request body using your secret and includes it in the Sangho-Signature header. You must recompute this signature on your server and compare it using a method resistant to timing attacks.

Use the raw body

The signature is computed on the raw body of the request, before any JSON parsing. In Express.js, configure express.raw({ type: 'application/json' }) on the webhook route — don’t use express.json(), which would transform the body.

Managing deliveries

The GET /webhooks/{id}/deliveries/ endpoint returns the history of delivery attempts for a given endpoint. If a delivery fails, you can retry it via POST /webhooks/{id}/deliveries/{did}/retry/.

Respond quickly, process asynchronously

Your endpoint must return a 200 within 10 seconds. For long-running processing (sending an email, calling a third-party service), queue the task (Celery, Bull, Sidekiq, etc.) and respond immediately. Past this delay, Sangho considers the delivery failed and triggers the retry policy.

Signature verification

python
# Verification on receipt (Python / Django)
import hmac, hashlib, os


def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)


@app.route('/webhooks/sangho', methods=['POST'])
def handle_webhook():
    payload = request.get_data()
    sig = request.headers.get('Sangho-Signature', '')


    if not verify_webhook(payload, sig, os.environ['SANGHO_WEBHOOK_SECRET']):
        return '', 403


    event = request.json
    if event['type'] == 'payment_intent.succeeded':
        order_id = event['data']['object']['metadata']['order_id']
        fulfill_order(order_id)


    return '', 200

Delivery logs

ResponseGET /webhooks/{id}/deliveries/
Returns delivery attempts with the HTTP status and your server's response.
json
{
  "object": "list",
  "data": [
    {
      "id": "del_xxxxxxxxxx",
      "event_type": "payment_intent.succeeded",
      "status": "succeeded",
      "http_status": 200,
      "attempts": 1,
      "duration_ms": 145,
      "delivered_at": "2026-03-01T10:05:01Z"
    },
    {
      "id": "del_yyyyyyyyyy",
      "event_type": "refund.succeeded",
      "status": "failed",
      "http_status": 500,
      "attempts": 3,
      "next_retry_at": "2026-03-01T12:05:00Z",
      "error": "Connection timeout"
    }
  ]
}