{"record":{"id":"65b7d8d46f0c2645","repo":"koala73/worldmonitor","slug":"service-temporarily-unavailable-register-webhook","errorCode":null,"errorMessage":"Service temporarily unavailable","messagePattern":"Service temporarily unavailable","errorType":"http","errorClass":"ApiError","httpStatus":503,"severity":"warning","filePath":"server/worldmonitor/shipping/v2/register-webhook.ts","lineNumber":52,"sourceCode":"): Promise<RegisterWebhookResponse> {\n  // Webhooks are per-tenant keyed on callerFingerprint(), which hashes the\n  // API key. Without forceKey, a Clerk-authenticated pro caller reaches this\n  // handler with no API key, callerFingerprint() falls back to 'anon', and\n  // every such caller collapses into a shared 'anon' owner bucket — letting\n  // one Clerk-session holder enumerate/overwrite other tenants' webhooks.\n  // Matches the legacy `api/v2/shipping/webhooks/[subscriberId]{,/[action]}.ts`\n  // gate and the documented \"X-WorldMonitor-Key required\" contract in\n  // docs/api-shipping-v2.mdx.\n  const apiKeyResult = (await validateApiKey(ctx.request, { forceKey: true })) as {\n    valid: boolean; required: boolean; error?: string; credential?: string;\n  };\n  if (apiKeyResult.error === USER_API_KEY_GATEWAY_VALIDATION_ERROR) {\n    const credential = getHeaderApiKey(ctx.request) as string;\n    let userKey;\n    try {\n      userKey = credential ? await validateUserApiKey(credential) : null;\n    } catch {\n      throw new ApiError(503, 'Service temporarily unavailable', '');\n    }\n    if (!userKey) throw new ApiError(401, 'Invalid API key', '');\n    // Revalidate the credential rather than trusting a caller-supplied user ID.\n    apiKeyResult.valid = true;\n    apiKeyResult.credential = credential;\n  }\n  if (apiKeyResult.required && !apiKeyResult.valid) {\n    throw new ApiError(401, apiKeyResult.error ?? 'API key required', '');\n  }\n\n  await requirePremiumRpcAccess(ctx.request, ApiError, 'PRO subscription required');\n\n  const callbackUrl = (req.callbackUrl ?? '').trim();\n  if (!callbackUrl) {\n    throw new ValidationError([{ field: 'callbackUrl', description: 'callbackUrl is required' }]);\n  }\n\n  try {","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/server/worldmonitor/shipping/v2/register-webhook.ts#L34-L70","documentation":"Same revalidation path as listWebhooks but in registerWebhook: when the gateway reports USER_API_KEY_GATEWAY_VALIDATION_ERROR, the server revalidates the header credential itself via validateUserApiKey. If that call throws (validation backend unavailable), the handler returns HTTP 503 'Service temporarily unavailable' rather than the internal failure.","triggerScenarios":"POSTing to register-webhook with a user API key during an outage of the key-validation service; validateUserApiKey throws inside the catch and the 503 is raised.","commonSituations":"Auth backend outage or timeout at peak load; network issue between the API server and the credential store; transient errors right after key rotation while caches propagate.","solutions":["Retry the registration after a short backoff — the 503 indicates a transient server-side condition","Check service health/status before repeated retries","Ensure the API key header is actually attached so validation behaves predictably","If the outage persists, delay webhook registration and re-run your setup script later"],"exampleFix":"// before\nawait client.registerWebhook({ callbackUrl }); // 503 during auth outage\n// after\nawait backoff(() => client.registerWebhook({ callbackUrl }), {\n  retries: 3,\n  retryOn: (e) => e.status === 503,\n});","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await client.registerWebhook(req);\n} catch (e) {\n  if (e.status === 503) {\n    await sleep(backoff(attempt++));\n    return registerWithRetry(req);\n  }\n  throw e;\n}","preventionTips":["Treat 503 on registration as transient; retry with backoff","Keep registration scripts idempotent so retries are safe","Alert on repeated 503s as a backend health signal"],"tags":["api","http-503","availability","webhook"],"backgroundTag":"upstream-api-error","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}