{"record":{"id":"0fb6e04df58234ac","repo":"nextauthjs/next-auth","slug":"missing-loops-api-key-or-transactionalid","errorCode":null,"errorMessage":"Missing Loops API Key or TransactionalId","messagePattern":"Missing Loops API Key or TransactionalId","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/core/src/providers/loops.ts","lineNumber":57,"sourceCode":" * })\n * ```\n *\n * @typedef LoopsUserConfig\n */\n\nexport default function Loops(config: LoopsUserConfig): LoopsConfig {\n  return {\n    id: \"loops\",\n    apiKey: \"\",\n    type: \"email\",\n    name: \"Loops\",\n    from: \"Auth.js <no-reply@authjs.dev>\",\n    maxAge: 24 * 60 * 60,\n    transactionalId: config.transactionalId || \"\",\n    async sendVerificationRequest(params: Params) {\n      const { identifier: to, provider, url } = params\n      if (!provider.apiKey || !provider.transactionalId)\n        throw new TypeError(\"Missing Loops API Key or TransactionalId\")\n\n      const res = await fetch(\"https://app.loops.so/api/v1/transactional\", {\n        method: \"POST\",\n        headers: {\n          Authorization: `Bearer ${provider.apiKey}`,\n          \"Content-Type\": \"application/json\",\n        },\n        body: JSON.stringify({\n          transactionalId: provider.transactionalId,\n          email: to,\n          dataVariables: {\n            url: url,\n          },\n        }),\n      })\n      if (!res.ok) {\n        throw new Error(\"Loops Send Error: \" + JSON.stringify(await res.json()))\n      }","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/nextauthjs/next-auth/blob/a1a16a5a7780488c7449feece410033f445d0b31/packages/core/src/providers/loops.ts#L39-L75","documentation":"The Loops provider throws a TypeError in sendVerificationRequest when either provider.apiKey or provider.transactionalId is falsy. Both are mandatory to call Loops' transactional email API, so the provider validates them up front before making any network request. This is a configuration error, not a runtime/network failure.","triggerScenarios":"Calling the Loops email provider's sendVerificationRequest with a config missing apiKey (e.g. process.env.LOOPS_API_KEY unset) or transactionalId (not passed and not defaulted) — the guard `if (!provider.apiKey || !provider.transactionalId)` fires immediately.","commonSituations":"Forgetting to set LOOPS_API_KEY in the deployment environment; creating the provider without a transactionalId because the developer hasn't created the transactional email template in Loops yet; typos in config keys so the fields land undefined.","solutions":["Set the API key: pass apiKey: process.env.LOOPS_API_KEY and ensure the env var exists where the app runs","Create a transactional email in Loops (with a {{url}} data variable) and pass its transactionalId to the provider","Double-check provider option key names (apiKey, transactionalId) match the provider's expected shape","Add a startup assertion/log that both fields are present before accepting traffic"],"exampleFix":"// before\nLoops({ transactionalId: config.transactionalId || \"\" }) // empty string -> throw\n// after\nLoops({\n  apiKey: process.env.LOOPS_API_KEY,\n  transactionalId: process.env.LOOPS_TRANSACTIONAL_ID, // set in env\n})","handlingStrategy":"validation","validationCode":"if (!process.env.LOOPS_API_KEY) throw new Error('LOOPS_API_KEY is required')\nif (!process.env.LOOPS_TRANSACTIONAL_ID) throw new Error('LOOPS_TRANSACTIONAL_ID is required')","typeGuard":"function isLoopsConfig(c: { apiKey?: string; transactionalId?: string }): c is { apiKey: string; transactionalId: string } {\n  return typeof c.apiKey === 'string' && c.apiKey.length > 0 && typeof c.transactionalId === 'string' && c.transactionalId.length > 0\n}","tryCatchPattern":"try {\n  await sendVerificationRequest(params)\n} catch (e) {\n  if (e instanceof TypeError && e.message.includes('Missing Loops API Key')) {\n    console.error('Loops provider misconfigured: set apiKey and transactionalId')\n  }\n}","preventionTips":["Create the transactional email in Loops first, then wire its transactionalId into config","Set LOOPS_API_KEY and the transactionalId in every deployment environment","Assert required provider options at app startup, before accepting sign-in traffic","Copy option key names exactly: apiKey, transactionalId"],"tags":["config","email","loops","missing-api-key"],"backgroundTag":"missing-env-var","analyzedSha":"a1a16a5a7780488c7449feece410033f445d0b31","analyzedAt":"2026-08-28T21:52:38.200Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}