{"record":{"id":"7e780be47ac858d0","repo":"hcengineering/platform","slug":"unauthorized-7e780b","errorCode":null,"errorMessage":"Unauthorized","messagePattern":"Unauthorized","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"services/mail/pod-mail/src/main.ts","lineNumber":91,"sourceCode":"  })\n  process.on('unhandledRejection', (e: any) => {\n    measureCtx.error(e.message)\n  })\n}\n\nexport async function handleSendMail (\n  client: MailClient,\n  req: Request,\n  res: Response,\n  ctx: MeasureContext\n): Promise<void> {\n  const { from, to, subject, text, html, attachments, headers, apiKey, password } = req.body\n  if (process.env.API_KEY !== undefined && process.env.API_KEY !== apiKey) {\n    ctx.warn('Unauthorized access attempt to send email', {\n      from,\n      to\n    })\n    res.status(401).send({ err: 'Unauthorized' })\n    return\n  }\n  const fromAddress = from ?? config.source\n  if (text === undefined && html === undefined) {\n    ctx.warn('Text and html are missing in email request', { from, to })\n    res.status(400).send({ err: \"'text' and 'html' are missing\" })\n    return\n  }\n  if (subject === undefined) {\n    ctx.warn('Subject is missing in email request', { from, to })\n    res.status(400).send({ err: \"'subject' is missing\" })\n    return\n  }\n  if (to === undefined) {\n    ctx.warn('To address is missing in email request', { from })\n    res.status(400).send({ err: \"'to' is missing\" })\n    return\n  }","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/services/mail/pod-mail/src/main.ts#L73-L109","documentation":"handleSendMail in pod-mail rejects with HTTP 401 { err: 'Unauthorized' } when the deployment defines the API_KEY environment variable and the request's apiKey field does not match it. The service is protected by a shared API key; sending without (or with a wrong) key is refused before any mail processing.","triggerScenarios":"POST to the mail endpoint while process.env.API_KEY is set on the pod-mail service and req.body.apiKey is undefined or different — e.g. client never configured the key, key rotated on the server, or the key is sent in a header instead of the body.","commonSituations":"New deployment set API_KEY env var but clients were not updated; key rotation propagated to server before clients; trailing whitespace/newline in the env value; client sends apiKey in an Authorization header but the service only reads it from the JSON body.","solutions":["Set body.apiKey in the request to exactly the value of the pod-mail API_KEY env var.","Verify no whitespace/quote differences between the configured env value and the sent key.","If the deployment should be open, remove the API_KEY env var from the pod-mail service.","If the key was rotated, redeploy/reconfigure clients with the new key."],"exampleFix":"// before\nawait fetch(mailUrl, { method: 'POST', body: JSON.stringify({ to, subject, text }) }) // 401\n// after\nawait fetch(mailUrl, {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ to, subject, text, apiKey: process.env.MAIL_API_KEY })\n})","handlingStrategy":"validation","validationCode":"const apiKey = process.env.MAIL_API_KEY\nif (apiKey == null || apiKey === '') throw new Error('MAIL_API_KEY not configured on client; server has API_KEY set and will 401')","typeGuard":"function hasApiKey(b: unknown): b is { apiKey: string } {\n  return typeof (b as any)?.apiKey === 'string' && (b as any).apiKey.length > 0\n}","tryCatchPattern":"const res = await fetch(mailUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...mail, apiKey }) })\nif (res.status === 401) throw new Error('mail API key rejected: check API_KEY on server vs apiKey in body')","preventionTips":["Keep client MAIL_API_KEY and server API_KEY in sync; update both during rotation.","Send apiKey as a top-level body field, not a header.","Trim env values to avoid whitespace mismatches.","If no auth is wanted, unset API_KEY on the pod-mail service instead of sending an empty key."],"tags":["http-401","api-key","authentication","mail"],"backgroundTag":"invalid-api-key","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}