Budibase/budibase · error · HTTPError
Invalid webhook schema token
Error message
Invalid webhook schema token
What it means
buildSchemaWithToken allows unauthenticated schema builds using a schemaToken stored on the webhook document. If the webhook does not exist, has no schemaToken, or the supplied token does not match, a 403 HTTPError is thrown.
Source
Thrown at packages/server/src/api/controllers/webhook.ts:132
const webhook = await db.tryGet<Webhook>(ctx.params.id)
if (!webhook) {
throw new HTTPError("Webhook not found", 404)
}
ctx.body = await updateWebhookSchema(ctx.request.body, webhook, db)
})
}
export async function buildSchemaWithToken(ctx: BuildSchemaWithTokenCtx) {
assertSchemaWorkspace(ctx.params.instance)
await context.doInWorkspaceContext(ctx.params.instance, async () => {
const db = context.getWorkspaceDB()
const webhook = await db.tryGet<Webhook>(ctx.params.id)
if (
!webhook?.schemaToken ||
webhook.schemaToken !== ctx.params.schemaToken
) {
throw new HTTPError("Invalid webhook schema token", 403)
}
ctx.body = await updateWebhookSchema(ctx.request.body, webhook, db)
})
}
export async function trigger(
ctx: Ctx<TriggerWebhookRequest, TriggerWebhookResponse>
) {
const prodAppId = dbCore.getProdWorkspaceID(ctx.params.instance)
const appNotDeployed = () => {
ctx.body = {
message: "Application not deployed yet.",
}
}
await context.doInWorkspaceContext(prodAppId, async () => {
const db = context.getWorkspaceDB()
const webhook = await db.tryGet<Webhook>(ctx.params.id)View on GitHub (pinned to a81a902e9a)
Solutions
- Re-fetch the webhook document (GET /webhooks) and use its current schemaToken in the URL.
- Recreate/re-save the webhook to generate a fresh schemaToken and update the external caller.
- Verify the request targets the correct workspace (instance param) that owns the webhook.
- If authentication is available, use the authenticated buildSchema endpoint instead of the token one.
Example fix
// before
curl -X POST https://host/api/webhooks/schema/{appId}/{webhookId}/{oldToken}
// after
curl -X POST https://host/api/webhooks/schema/{appId}/{webhookId}/{currentSchemaToken} Defensive patterns
Strategy: validation
Validate before calling
const webhook = await api.get(`/webhooks`).then(r => r.data.find(w => w._id === webhookId))
if (!webhook?.schemaToken) throw new Error("No schemaToken on webhook; re-save it to generate one") Type guard
const hasValidToken = (w?: { schemaToken?: string }, token?: string): boolean =>
Boolean(w?.schemaToken && w.schemaToken === token) Try / catch
try {
await api.post(`/webhooks/schema/${appId}/${webhookId}/${token}`)
} catch (e) {
if (e.status === 403 && /schema token/.test(e.message)) {
// refetch webhook doc, rotate the stored token URL, retry once
} else throw e
} Prevention
- Store the schemaToken URL alongside the webhook config and refresh it on webhook re-save.
- Prefer the authenticated schema build endpoint when credentials are available.
- Regenerate external tool configs whenever the webhook is recreated.
- Confirm the instance param targets the owning app before debugging tokens.
When it happens
Trigger: Calling the token-based schema build endpoint with an expired/rotated schemaToken, a token from another webhook, or before the webhook has ever generated a token.
Common situations: Stale URLs saved in external tools (e.g. curl examples) after the webhook was recreated; token regenerated after re-saving the webhook; calling against the wrong app so the webhook (and its token) is not found.
Related errors
- Forbidden
- CouchDB password not set
- Access denied to object store bucket.${err}
- userId is required
- channel.provider is required
AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29).
Data as JSON: /api/errors/a8cccc5d1db3ddbf.
Report an issue: GitHub.