{"record":{"id":"a8cccc5d1db3ddbf","repo":"Budibase/budibase","slug":"invalid-webhook-schema-token","errorCode":null,"errorMessage":"Invalid webhook schema token","messagePattern":"Invalid webhook schema token","errorType":"http","errorClass":"HTTPError","httpStatus":403,"severity":"error","filePath":"packages/server/src/api/controllers/webhook.ts","lineNumber":132,"sourceCode":"    const webhook = await db.tryGet<Webhook>(ctx.params.id)\n    if (!webhook) {\n      throw new HTTPError(\"Webhook not found\", 404)\n    }\n    ctx.body = await updateWebhookSchema(ctx.request.body, webhook, db)\n  })\n}\n\nexport async function buildSchemaWithToken(ctx: BuildSchemaWithTokenCtx) {\n  assertSchemaWorkspace(ctx.params.instance)\n\n  await context.doInWorkspaceContext(ctx.params.instance, async () => {\n    const db = context.getWorkspaceDB()\n    const webhook = await db.tryGet<Webhook>(ctx.params.id)\n    if (\n      !webhook?.schemaToken ||\n      webhook.schemaToken !== ctx.params.schemaToken\n    ) {\n      throw new HTTPError(\"Invalid webhook schema token\", 403)\n    }\n\n    ctx.body = await updateWebhookSchema(ctx.request.body, webhook, db)\n  })\n}\n\nexport async function trigger(\n  ctx: Ctx<TriggerWebhookRequest, TriggerWebhookResponse>\n) {\n  const prodAppId = dbCore.getProdWorkspaceID(ctx.params.instance)\n  const appNotDeployed = () => {\n    ctx.body = {\n      message: \"Application not deployed yet.\",\n    }\n  }\n  await context.doInWorkspaceContext(prodAppId, async () => {\n    const db = context.getWorkspaceDB()\n    const webhook = await db.tryGet<Webhook>(ctx.params.id)","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/server/src/api/controllers/webhook.ts#L114-L150","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncurl -X POST https://host/api/webhooks/schema/{appId}/{webhookId}/{oldToken}\n// after\ncurl -X POST https://host/api/webhooks/schema/{appId}/{webhookId}/{currentSchemaToken}","handlingStrategy":"validation","validationCode":"const webhook = await api.get(`/webhooks`).then(r => r.data.find(w => w._id === webhookId))\nif (!webhook?.schemaToken) throw new Error(\"No schemaToken on webhook; re-save it to generate one\")","typeGuard":"const hasValidToken = (w?: { schemaToken?: string }, token?: string): boolean =>\n  Boolean(w?.schemaToken && w.schemaToken === token)","tryCatchPattern":"try {\n  await api.post(`/webhooks/schema/${appId}/${webhookId}/${token}`)\n} catch (e) {\n  if (e.status === 403 && /schema token/.test(e.message)) {\n    // refetch webhook doc, rotate the stored token URL, retry once\n  } else throw e\n}","preventionTips":["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."],"tags":["webhook","auth","token","http-403"],"backgroundTag":"invalid-credentials","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}