Webhooks
Webhooks let you receive events instantly instead of continuously polling your
server. When an event occurs, Rabisu sends a signed POST request to your
registered URL.
1. Register an Endpoint — POST /webhooks
curl -X POST https://api.rabisu.com/api/v1/webhooks \
-u "rsk_live_xxx:rsks_yyy" \
-H "Content-Type: application/json" \
-d '{
"url": "https://reseller.com/modules/addons/rabisu/webhook.php",
"event_types": ["service.*", "credit.added"],
"description": "Production webhook"
}'
The HMAC secret is returned only once — store it securely:
{
"success": true,
"data": {
"id": 12,
"url": "https://reseller.com/.../webhook.php",
"event_types": ["service.*", "credit.added"],
"secret": "whsec_...ONE_TIME...",
"is_active": true
},
"meta": { "trace_id": "...", "timestamp": "..." }
}
URL Rules (SSRF Protection)
https://is required.- The URL may not contain
user:pass@. - All resolved DNS IPs must be public (private/internal IPs are rejected).
2. Subscription Patterns
The event_types array supports these forms:
| Pattern | Meaning |
|---|---|
* | All events (catch-all) |
service.* | Namespace wildcard — all events starting with service |
service.created | Exact event name |
Valid namespaces: service, product, credit. Sending an invalid pattern makes
the registration fail with 422 (a clear error instead of silently "receiving no
events").
3. Event Catalog
| Event | When | Payload fields (summary) |
|---|---|---|
service.created | VPS provisioning completed (active) | provision_id, service_id, order_id, product_id, status |
service.suspended | Service suspended | service_id, reason |
service.unsuspended | Suspension lifted | service_id |
service.terminated | Service terminated | service_id |
product.price_changed | Product price changed (notification) | product_id |
product.stock_changed | Product stock status changed | product_id, stock_active |
product.disabled | Product closed for sale | product_id |
credit.added | Reseller credit added (refund/manual) | reseller_id, amount, currency, reason, new_balance |
Note:
product.*events carry a notification only. Fetch the current data withGET /products/{id}(hybrid notification + pull).
Every payload includes a schema_version field at its root (currently 1).
4. Signature Verification (CRITICAL)
Every delivery includes two signature headers:
| Header | Content | Usage |
|---|---|---|
X-Rabisu-Signature-V2 | t=<unix>,v1=<hex> — HMAC-SHA256(secret, "{t}.{X-Rabisu-Event-Type}.{raw body}") | Recommended. Protects against replay and event-type swapping |
X-Rabisu-Signature | sha256=<hex> — HMAC-SHA256 of the raw body only | For legacy receivers. No replay protection; do not use in new integrations |
The receiver must verify that |now - t| <= 300 seconds and check the signed
event type (X-Rabisu-Event-Type).
Verification (PHP, V2):
$raw = file_get_contents('php://input');
$eventType = $_SERVER['HTTP_X_RABISU_EVENT_TYPE'] ?? '';
$header = $_SERVER['HTTP_X_RABISU_SIGNATURE_V2'] ?? '';
parse_str(str_replace(',', '&', $header), $parts); // t=..., v1=...
$t = (int) ($parts['t'] ?? 0);
$v1 = (string) ($parts['v1'] ?? '');
if (abs(time() - $t) > 300) {
http_response_code(401); // Outside the time window (replay)
exit;
}
$expected = hash_hmac('sha256', $t . '.' . $eventType . '.' . $raw, $webhookSecret);
if (!hash_equals($expected, $v1)) {
http_response_code(401); // Invalid signature
exit;
}
// Safe: the payload can be processed
$payload = json_decode($raw, true);
- Always verify the signature — otherwise you accept forged requests.
- Use a constant-time comparison such as
hash_equals. - Verify against the raw body before decoding to JSON (re-serialized JSON breaks the signature).
5. Delivery and Response Contract
Your endpoint should return the following HTTP codes:
| Response | Meaning | Rabisu behavior |
|---|---|---|
2xx | Processed successfully | Delivery complete |
4xx (incl. 401) | Permanent error (signature/format) | Not retried |
5xx | Transient error | Retried with exponential backoff |
Retry schedule: 1m → 5m → 15m → 1h → 6h → 24h. After 6 attempts the event is marked
"dead letter". After 5 consecutive failures the endpoint is automatically
deactivated (circuit breaker) — reactivate it with PATCH /webhooks/{id} once fixed.
6. Management Endpoints
GET /webhooks— list registered endpoints (including inactive)GET /webhooks/{id}— detailPATCH /webhooks/{id}— reactivate / updateevent_typesordescriptionDELETE /webhooks/{id}— delete (soft delete)