Reference
Method: POST Path: /public/v1/orgs/:orgId/entities/:entityId/webhooks Auth: Bearer marketplace token Tenant guard: :orgId and :entityId MUST match the install scope Required scope: none beyond a valid install Rate limit: standard
Request body
{
"topics": ["invoice.paid", "bill.created"],
"target_url": "https://your-app.example.com/hellobooks/webhook"
}| Field | Type | Required | Validation |
|---|---|---|---|
topics | string[] | yes | At least one element. Each is lowercased server-side. |
target_url | string | yes | Must start with https://. HTTP is rejected with 400. |
Response (201)
{
"id": "65f1aa...",
"topics": ["invoice.paid", "bill.created"],
"target_url": "https://your-app.example.com/hellobooks/webhook",
"signing_secret": "shhh-this-is-shown-once-base64",
"created_at": "2026-05-04T10:14:32.123Z"
}⚠️ signing_secret is the plaintext secret. It is bcrypt-hashed server-side and never returned again. Store it immediately and treat it like a password.
Errors
| HTTP | error | When |
|---|---|---|
| 400 | invalid_request | topics[] empty/missing, or target_url not HTTPS |
| 401 | invalid_token | Standard auth failures |
| 401 | invalid_install | Install Status is not active |
| 403 | tenant_scope_violation | URL params don't match install scope |
Curl example
curl -s -X POST \
-H "Authorization: Bearer $HB_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"topics": ["invoice.paid"],
"target_url": "https://your-app.example.com/hellobooks/webhook"
}' \
https://api.hellobooks.com/public/v1/orgs/$HB_ORG/entities/$HB_ENT/webhooks | jq