koala73/worldmonitor · error · ApiError

API key required

Error message

API key required

What it means

registerWebhook requires an explicit API key via validateApiKey({ forceKey: true }), matching the legacy api/v2/shipping/webhooks/[subscriberId] gate and the documented X-WorldMonitor-Key contract. The key is required because webhook ownership is keyed on callerFingerprint() (a hash of the API key); a keyless Clerk pro caller would fall into the shared 'anon' bucket and could enumerate or overwrite other tenants' webhooks, so the handler rejects with 401 first.

Source

Thrown at server/worldmonitor/shipping/v2/register-webhook.ts:45

} from './webhook-shared';

export async function registerWebhook(
  ctx: ServerContext,
  req: RegisterWebhookRequest,
): Promise<RegisterWebhookResponse> {
  // Webhooks are per-tenant keyed on callerFingerprint(), which hashes the
  // API key. Without forceKey, a Clerk-authenticated pro caller reaches this
  // handler with no API key, callerFingerprint() falls back to 'anon', and
  // every such caller collapses into a shared 'anon' owner bucket — letting
  // one Clerk-session holder enumerate/overwrite other tenants' webhooks.
  // Matches the legacy `api/v2/shipping/webhooks/[subscriberId]{,/[action]}.ts`
  // gate and the documented "X-WorldMonitor-Key required" contract in
  // docs/api-shipping-v2.mdx.
  const apiKeyResult = (await validateApiKey(ctx.request, { forceKey: true })) as {
    valid: boolean; required: boolean; error?: string; credential?: string;
  };
  if (apiKeyResult.required && !apiKeyResult.valid) {
    throw new ApiError(401, apiKeyResult.error ?? 'API key required', '');
  }

  await requirePremiumRpcAccess(ctx.request, ApiError, 'PRO subscription required');

  const callbackUrl = (req.callbackUrl ?? '').trim();
  if (!callbackUrl) {
    throw new ValidationError([{ field: 'callbackUrl', description: 'callbackUrl is required' }]);
  }

  try {
    await assertCallbackUrlRegistrationSafe(callbackUrl);
  } catch (error) {
    const message = error instanceof Error ? error.message : 'callbackUrl is not allowed';
    throw new ValidationError([{ field: 'callbackUrl', description: message }]);
  }

  const chokepointIds = Array.isArray(req.chokepointIds) ? req.chokepointIds : [];
  const invalidCp = chokepointIds.find(id => !VALID_CHOKEPOINT_IDS.has(id));

View on GitHub (pinned to eeab0a219f)

Solutions

  1. Obtain a shipping v2 API key and send it as the X-WorldMonitor-Key header on RegisterWebhook calls
  2. Validate the key is current and active; re-issue if rotated or revoked
  3. Ensure server-to-server callers load the key from configuration at startup, not per-request from an optional env

Example fix

// before
await shippingClient.registerWebhook({ callbackUrl, chokepointIds }); // 401
// after
await shippingClient.registerWebhook(
  { callbackUrl, chokepointIds },
  { headers: { 'X-WorldMonitor-Key': process.env.WORLDMONITOR_API_KEY! } },
);
Defensive patterns

Strategy: validation

Validate before calling

if (!process.env.WORLDMONITOR_API_KEY) throw new Error('X-WorldMonitor-Key missing — required by registerWebhook even for Clerk-authenticated callers');

Try / catch

catch (e) { if (e?.status === 401) { surface a configuration error for the API key; stop retrying } else throw e; }

Prevention

When it happens

Trigger: POST RegisterWebhook without the X-WorldMonitor-Key header while relying on a Clerk session; invalid/expired/revoked key; automated partner client that never had the key configured. The check runs before premium gating, callbackUrl validation, and the SSRF safety check.

Common situations: Partner onboarding that skipped key issuance; key rotated and the old one cached in the client; env var for the key missing in CI; frontend code accidentally calling the RPC with session cookies only.

Understand the failure class

Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21). Data as JSON: /api/errors/9a54453b7ac7c885. Report an issue: GitHub.