1. Documentation
  2. Developers
  3. 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
}
FieldTypePurpose
activebooleantrue opens the entitlement, false closes it. Default: true
tierstringThe tier's key. Optional if there is only one tier
discordIdstringThe member's Discord ID
emailstringThe buyer's email, for claiming
refstringYour reference (subscription ID, order ID…)
lifetimebooleanLifetime access, never removed automatically
expiresAtISO 8601The entitlement lapses on its own at this date. Ignored if lifetime
graceDaysintegerForces 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, and discordId or email.
  • Closing (active: false) only requires finding the entitlement: ref is 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

CodeMeaning
202Processed. The body contains the result
400Incomplete request or unknown tier
401Invalid secret
404Feature, source or secret not configured on this server
500Internal error. The call can be replayed safely

Possible results inside a 202:

ResultMeaning
grantedEntitlement opened, role granted
pending_claimEntitlement created as pending: the person must claim it in Discord
grace_scheduledRemoval scheduled after the grace period
revokedEntitlement closed, role removed
not_foundNo 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 202 does 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.