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

  1. Re-fetch the webhook document (GET /webhooks) and use its current schemaToken in the URL.
  2. Recreate/re-save the webhook to generate a fresh schemaToken and update the external caller.
  3. Verify the request targets the correct workspace (instance param) that owns the webhook.
  4. 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

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


AI-assisted analysis of Budibase/budibase@a81a902e9a (2026-08-29). Data as JSON: /api/errors/a8cccc5d1db3ddbf. Report an issue: GitHub.