Webhooks
Receive real-time notifications when events happen — invoices paid, services activated, tickets opened, and more.
Webhooks notify your systems in real time when events occur on the OpenHost platform. Instead of polling the API, register an endpoint and we push events to you.
How it works
- You register a webhook URL in the client area
- When an event occurs, we POST a signed JSON payload to your URL
- You verify the signature and process the event
- Respond with
200to acknowledge receipt
Register a webhook
Client area → Profile → API Credentials → Webhooks → Add endpoint.
| Field | Description |
|---|---|
| URL | Your HTTPS endpoint (must respond within 10s) |
| Events | Which event types to subscribe to |
| Secret | Auto-generated HMAC signing key (shown once) |
Available events
| Event | Fires when |
|---|---|
invoice.paid | An invoice is marked paid (any method) |
invoice.overdue | Invoice passes its due date |
service.activated | A hosting service is provisioned and active |
service.suspended | Service is suspended (non-payment, abuse, etc.) |
service.cancelled | Service is cancelled and deprovisioned |
service.upgraded | Plan upgrade applied |
client.created | New client account registered |
ticket.opened | Support ticket created |
ticket.replied | New reply on a ticket |
Payload format
All events share a common envelope:
{
"event": "invoice.paid",
"timestamp": "2026-07-04T10:15:30Z",
"data": {
"id": 1234,
"client_id": 42,
"amount": "39.00",
"currency": "ILS",
"items": [
{ "title": "WP Growth - Monthly", "amount": "39.00" }
]
}
}The data field varies by event type. See examples below.
Verifying signatures
Every webhook request includes an X-OpenHost-Signature header containing an HMAC-SHA256 hex digest of the raw request body using your webhook secret:
X-OpenHost-Signature: sha256=a1b2c3d4e5f6...
Verification (Node.js):
import { createHmac, timingSafeEqual } from "crypto";
function verifyWebhook(body, signature, secret) {
const expected = "sha256=" + createHmac("sha256", secret)
.update(body, "utf8")
.digest("hex");
return timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Verification (Python):
import hmac, hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)Retry policy
If your endpoint doesn't respond with 2xx within 10 seconds:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 12 hours |
After 5 failed retries, the event is marked as failed. You can manually replay failed events from the webhook dashboard.
Idempotency
Events include a unique event_id field. Your handler should be idempotent — processing the same event twice should have no additional effect. Store processed event_ids and skip duplicates.
Event examples
service.activated
{
"event": "service.activated",
"event_id": "evt_abc123",
"timestamp": "2026-07-04T10:15:30Z",
"data": {
"service_id": 101,
"client_id": 42,
"product": "WP Growth",
"domain": "example.com",
"server": "node1.openhost.one"
}
}ticket.opened
{
"event": "ticket.opened",
"event_id": "evt_def456",
"timestamp": "2026-07-04T11:00:00Z",
"data": {
"ticket_id": 789,
"client_id": 42,
"subject": "Cannot access wp-admin",
"department": "technical",
"priority": "medium"
}
}Testing webhooks
Use a tool like webhook.site or requestbin.com to inspect payloads during development. Then implement your handler and switch to your production URL.
Best practices
- Respond fast — do heavy processing asynchronously (queue the event, return 200 immediately)
- Verify signatures — reject unsigned or incorrectly signed payloads
- Handle retries — be idempotent; don't charge a customer twice
- Monitor failures — check the webhook dashboard for failed deliveries
- Use HTTPS — required, non-negotiable