twentyhq/twenty · critical · Error
RECALL_WEBHOOK_SECRET server variable is not set. A server a
Error message
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.
What it means
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`.
Source
Thrown at packages/twenty-apps/public/call-recorder/src/logic-functions/recall-webhook.ts:28
import { getApplicationVariableValue } from 'src/logic-functions/utils/get-application-variable-value.util';
import { isNonEmptyString } from 'src/logic-functions/utils/is-non-empty-string.util';
type RecallWebhookResolverResult = {
workspaceId: string;
targetLogicFunctionUniversalIdentifier: string;
payload: RecallWebhookBody;
};
// A thrown error becomes a non-2xx, which makes Svix retry; a returned result dispatches to the target.
export const recallWebhookRouteHandler = (
routePayload: RoutePayload<RecallWebhookBody>,
): RecallWebhookResolverResult => {
const webhookSecret = getApplicationVariableValue(
RECALL_WEBHOOK_SECRET_ENV_VAR_NAME,
);
if (!isNonEmptyString(webhookSecret)) {
throw new Error(
'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.',
);
}
const { rawBody } = routePayload;
if (isUndefined(rawBody)) {
throw new Error(
'Raw request body was not forwarded by the server; cannot verify the webhook signature',
);
}
const signatureCheck = verifyRecallWebhookSignature({
rawBody,
headers: routePayload.headers,
secret: webhookSecret,
});
View on GitHub (pinned to 1f5dd2bbd2)
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).
Example fix
// before
const webhookSecret = getApplicationVariableValue(RECALL_WEBHOOK_SECRET_ENV_VAR_NAME);
if (!isNonEmptyString(webhookSecret)) {
throw new Error('RECALL_WEBHOOK_SECRET server variable is not set. ...');
}
// after — no code change fixes this; it is an ops task. Document the runbook in the error:
if (!isNonEmptyString(webhookSecret)) {
throw new Error(
`${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.`,
);
} Defensive patterns
Strategy: validation
Validate before calling
// Health check the route can call before accepting webhooks: export const isRecallWebhookConfigured = (): boolean => isNonEmptyString(getApplicationVariableValue(RECALL_WEBHOOK_SECRET_ENV_VAR_NAME)); // UI/install hook can surface this so admins set the secret before enabling the route.
Type guard
const hasWebhookSecret = (value: unknown): value is string => typeof value === 'string' && value.trim().length > 0;
Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Raw request body was not forwarded by the server; cannot ver
- Invalid webhook signature: ${signatureCheck.error}
- failed to request artifact import for call recording ${callR
- download failed with status ${response.status}
- download response is missing content-length
AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12).
Data as JSON: /api/errors/65c6988c3ef5e61c.
Report an issue: GitHub.