- Documentation
- Developers
- Webhooks
Premium webhook
Full reference: endpoint, authentication, body, responses.
The direct webhook is the generic pipe for premium roles. Any source can trigger it: your site, a game backend, a Make or Zapier bridge, a selling platform.
Endpoint
POST /webhooks/premium/{guildId}
Content-Type: application/json
Authorization: Bearer <server secret>The secret belongs to each server. It is generated from the dashboard, shown once, and can be regenerated at will. The x-premium-secret header is accepted as an alternative to Authorization.
Request body
{
"tier": "gold",
"active": true,
"discordId": "123456789012345678",
"email": "customer@example.com",
"ref": "sub_42",
"lifetime": false,
"expiresAt": "2026-12-31T23:59:59Z",
"graceDays": 7
}| Field | Type | Purpose |
|---|---|---|
active | boolean | true opens the entitlement, false closes it. Default: true |
tier | string | The tier's key. Optional if there is only one tier |
discordId | string | The member's Discord ID |
email | string | The buyer's email, for claiming |
ref | string | Your reference (subscription ID, order ID…) |
lifetime | boolean | Lifetime access, never removed automatically |
expiresAt | ISO 8601 | The entitlement lapses on its own at this date. Ignored if lifetime |
graceDays | integer | Forces the grace period on removal. 0 = immediate |
The state-based model
There is no action, event or status field. The only discriminator is active. You describe the desired state, Bao reconciles.
- Opening (
active: true) requires a tier, anddiscordIdoremail. - Closing (
active: false) only requires finding the entitlement:refis enough, and it is the recommended route. You do not have to keep an identity around just to cancel.
Idempotence
Provide ref. Callbacks carrying the same reference update the same entitlement. Replaying a grant never creates a duplicate: you can retry a call if in doubt.
Responses
| Code | Meaning |
|---|---|
202 | Processed. The body contains the result |
400 | Incomplete request or unknown tier |
401 | Invalid secret |
404 | Feature, source or secret not configured on this server |
500 | Internal error. The call can be replayed safely |
Possible results inside a 202:
| Result | Meaning |
|---|---|
granted | Entitlement opened, role granted |
pending_claim | Entitlement created as pending: the person must claim it in Discord |
grace_scheduled | Removal scheduled after the grace period |
revoked | Entitlement closed, role removed |
not_found | No matching entitlement to remove |
Examples
Opening access for a known member:
curl -X POST https://<bao-url>/webhooks/premium/123456789012345678 \
-H "Authorization: Bearer bao_prm_..." \
-H "Content-Type: application/json" \
-d '{"tier":"gold","active":true,"discordId":"987654321098765432","ref":"sub_42"}'Opening access for a buyer who only gave their email:
curl -X POST https://<bao-url>/webhooks/premium/123456789012345678 \
-H "Authorization: Bearer bao_prm_..." \
-H "Content-Type: application/json" \
-d '{"tier":"gold","active":true,"email":"customer@example.com","ref":"order_1042"}'Closing access:
curl -X POST https://<bao-url>/webhooks/premium/123456789012345678 \
-H "Authorization: Bearer bao_prm_..." \
-H "Content-Type: application/json" \
-d '{"active":false,"ref":"sub_42"}'Traps worth knowing
- A 404 is not a code error. It means the feature is switched off, the source is disabled, or no secret was generated. Check the configuration before hunting on the client side.
- A member absent from the server is not a failure. The entitlement is kept and the role is applied when they arrive. Do not implement a queue.
- A
202does not guarantee the role was applied. If Bao's role sits below the premium role, the grant fails and only shows up in the premium audit log. This is the number one cause of missing roles.
Delivery log
Every call received is recorded with its result and a readable explanation: invalid secret, unknown tier, entitlement already removed. You can diagnose a 401 or a 404 yourself from the dashboard, without writing to us.