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

  1. 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).
  2. Confirm the variable name matches `RECALL_WEBHOOK_SECRET_ENV_VAR_NAME` exactly and that it is set at server scope, not workspace scope.
  3. After setting, send a test webhook from Recall (or the Svix dashboard) and confirm a 2xx response.
  4. 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

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


AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12). Data as JSON: /api/errors/65c6988c3ef5e61c. Report an issue: GitHub.