{"record":{"id":"65c6988c3ef5e61c","repo":"twentyhq/twenty","slug":"recall-webhook-secret-server-variable-is-not-set","errorCode":null,"errorMessage":"RECALL_WEBHOOK_SECRET server variable is not set. A server admin must copy it from the Recall webhook endpoint settings and set it on the Call Recorder application registration.","messagePattern":"RECALL_WEBHOOK_SECRET server variable is not set\\. A server admin must copy it from the Recall webhook endpoint settings and set it on the Call Recorder application registration\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/twenty-apps/public/call-recorder/src/logic-functions/recall-webhook.ts","lineNumber":28,"sourceCode":"import { getApplicationVariableValue } from 'src/logic-functions/utils/get-application-variable-value.util';\nimport { isNonEmptyString } from 'src/logic-functions/utils/is-non-empty-string.util';\n\ntype RecallWebhookResolverResult = {\n  workspaceId: string;\n  targetLogicFunctionUniversalIdentifier: string;\n  payload: RecallWebhookBody;\n};\n\n// A thrown error becomes a non-2xx, which makes Svix retry; a returned result dispatches to the target.\nexport const recallWebhookRouteHandler = (\n  routePayload: RoutePayload<RecallWebhookBody>,\n): RecallWebhookResolverResult => {\n  const webhookSecret = getApplicationVariableValue(\n    RECALL_WEBHOOK_SECRET_ENV_VAR_NAME,\n  );\n\n  if (!isNonEmptyString(webhookSecret)) {\n    throw new Error(\n      'RECALL_WEBHOOK_SECRET server variable is not set. A server admin must copy it from the Recall webhook endpoint settings and set it on the Call Recorder application registration.',\n    );\n  }\n\n  const { rawBody } = routePayload;\n\n  if (isUndefined(rawBody)) {\n    throw new Error(\n      'Raw request body was not forwarded by the server; cannot verify the webhook signature',\n    );\n  }\n\n  const signatureCheck = verifyRecallWebhookSignature({\n    rawBody,\n    headers: routePayload.headers,\n    secret: webhookSecret,\n  });\n","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-apps/public/call-recorder/src/logic-functions/recall-webhook.ts#L10-L46","documentation":"The Recall webhook route handler reads the `RECALL_WEBHOOK_SECRET` application variable (a server-scoped secret used to verify webhook signatures). If it is absent or blank, the handler throws immediately — it cannot safely verify any incoming webhook without the secret. Because the route is wired so that a throw becomes a non-2xx (causing Svix to retry), this error also means every webhook will fail-and-retry until the secret is set. This is a configuration error flagged via the SDK's `getApplicationVariableValue`.","triggerScenarios":"The Call Recorder app registration never had `RECALL_WEBHOOK_SECRET` set, the variable was cleared/renamed, or it was set on the wrong scope (workspace instead of server). The check runs on every inbound Recall webhook.","commonSituations":"First deployment of the Call Recorder app without completing the secret setup; copying the app config but forgetting the server variable; rotating the Recall webhook endpoint and not copying the new secret into the app registration.","solutions":["Copy the webhook signing secret from the Recall.ai webhook endpoint settings and set it as the `RECALL_WEBHOOK_SECRET` application variable on the Call Recorder registration (server scope).","Confirm the variable name matches `RECALL_WEBHOOK_SECRET_ENV_VAR_NAME` exactly and that it is set at server scope, not workspace scope.","After setting, send a test webhook from Recall (or the Svix dashboard) and confirm a 2xx response.","If you intentionally want to disable the route, uninstall/pause the logic function rather than leaving the secret unset (which produces retry storms)."],"exampleFix":"// before\nconst webhookSecret = getApplicationVariableValue(RECALL_WEBHOOK_SECRET_ENV_VAR_NAME);\nif (!isNonEmptyString(webhookSecret)) {\n  throw new Error('RECALL_WEBHOOK_SECRET server variable is not set. ...');\n}\n\n// after — no code change fixes this; it is an ops task. Document the runbook in the error:\nif (!isNonEmptyString(webhookSecret)) {\n  throw new Error(\n    `${RECALL_WEBHOOK_SECRET_ENV_VAR_NAME} is not set (server scope). Runbook: Recall.ai → webhook endpoint → copy signing secret → Call Recorder app registration → server variables. Until set, all webhooks will fail and Svix will retry.`,\n  );\n}","handlingStrategy":"validation","validationCode":"// Health check the route can call before accepting webhooks:\nexport const isRecallWebhookConfigured = (): boolean =>\n  isNonEmptyString(getApplicationVariableValue(RECALL_WEBHOOK_SECRET_ENV_VAR_NAME));\n// UI/install hook can surface this so admins set the secret before enabling the route.","typeGuard":"const hasWebhookSecret = (value: unknown): value is string =>\n  typeof value === 'string' && value.trim().length > 0;","tryCatchPattern":null,"preventionTips":["Treat the webhook secret as a required install step; surface a health check in the app UI.","Set `RECALL_WEBHOOK_SECRET` at server scope with the exact name the route reads.","After any Recall endpoint rotation, copy the new signing secret into the app variable immediately.","Monitor the route for non-2xx — a surge indicates the secret went missing."],"tags":["webhook","recall","call-recorder","configuration","secrets","security"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}