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
- Obtain a shipping v2 API key and send it as the X-WorldMonitor-Key header on RegisterWebhook calls
- Validate the key is current and active; re-issue if rotated or revoked
- 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
- Attach X-WorldMonitor-Key on every registerWebhook/listWebhooks call via a shared client wrapper
- Store the key in a secret manager and validate presence at boot
- Key rotation: update clients atomically before revoking the old key
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
- API key required
- unauthorized
- Unknown chokepoint ID: ${invalidCp}
- UNAUTHENTICATED
- INVALID_API_KEY_SCOPES
AI-assisted analysis of koala73/worldmonitor@eeab0a219f (2026-08-21).
Data as JSON: /api/errors/9a54453b7ac7c885.
Report an issue: GitHub.