koala73/worldmonitor · error · ApiError
API key required
Error message
API key required
What it means
listWebhooks calls validateApiKey with forceKey: true, so an explicit shipping v2 API key is mandatory even for Clerk-authenticated PRO callers. Without a key, callerFingerprint() would collapse to the shared 'anon' bucket and the ownerTag !== ownerHash defense would degenerate (both sides 'anon'), exposing every anon-bucket tenant's webhooks — hence the up-front 401.
Source
Thrown at server/worldmonitor/shipping/v2/list-webhooks.ts:35
ownerIndexKey,
callerFingerprint,
type WebhookRecord,
} from './webhook-shared';
export async function listWebhooks(
ctx: ServerContext,
_req: ListWebhooksRequest,
): Promise<ListWebhooksResponse> {
// Without forceKey, Clerk-authenticated pro callers reach this handler with
// no API key, callerFingerprint() returns the 'anon' fallback, and the
// ownerTag !== ownerHash defense-in-depth below collapses because both
// sides equal 'anon' — exposing every 'anon'-bucket tenant's webhooks to
// every Clerk-session holder. See registerWebhook for full rationale.
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 ownerHash = await callerFingerprint(ctx.request, apiKeyResult.credential);
const smembersResult = await runRedisPipeline([['SMEMBERS', ownerIndexKey(ownerHash)]]);
const memberIds = (smembersResult[0]?.result as string[] | null) ?? [];
if (memberIds.length === 0) {
return { webhooks: [] };
}
const getResults = await runRedisPipeline(memberIds.map(id => ['GET', webhookKey(id)]));
const webhooks: WebhookSummary[] = [];
for (const r of getResults) {
if (!r.result || typeof r.result !== 'string') continue;
try {
const record = JSON.parse(r.result) as WebhookRecord;View on GitHub (pinned to eeab0a219f)
Solutions
- Send the shipping v2 API key header (X-WorldMonitor-Key, per docs/api-shipping-v2.mdx) on every ListWebhooks and RegisterWebhook call
- Confirm the key is active — re-issue from the dashboard if revoked or expired
- Keep sending the key even when already Clerk-authenticated; the session alone is intentionally insufficient for this handler
Example fix
// before
await shippingClient.listWebhooks({}); // 401: API key required
// after
await shippingClient.listWebhooks({}, {
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 — issue and configure a shipping v2 key before calling listWebhooks'); Try / catch
catch (e) { if (e?.status === 401) { halt and prompt for API key provisioning; do not retry with the same credentials } else throw e; } Prevention
- Wrap all shipping v2 RPC calls in a client that always attaches X-WorldMonitor-Key
- Fail fast at startup when the key env var is absent
- Never assume a Clerk session authorizes webhook RPCs — the key is mandatory by design
When it happens
Trigger: Calling ListWebhooks with only a Clerk session and no X-WorldMonitor-Key header; sending an invalid, revoked, or expired API key; a server-side script that never loaded the key. The 401 fires before requirePremiumRpcAccess and before the SMEMBERS owner-index read.
Common situations: Integration reusing the browser session token instead of the issued API key; key rotated or revoked server-side; local dev .env missing the key; curl testing without the header; assuming PRO subscription alone authorizes webhook RPCs.
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/09ee74792990d896.
Report an issue: GitHub.