{"record":{"id":"8a953f9ce32cbb99","repo":"koala73/worldmonitor","slug":"api-access-required","errorCode":"API_ACCESS_REQUIRED","errorMessage":"API_ACCESS_REQUIRED","messagePattern":"API_ACCESS_REQUIRED","errorType":"error_code","errorClass":"ConvexError","httpStatus":null,"severity":"error","filePath":"convex/apiKeys.ts","lineNumber":64,"sourceCode":"    keyHash: v.string(),\n    scopes: v.optional(v.array(v.string())),\n  },\n  handler: async (ctx, args) => {\n    const userId = await requireUserId(ctx);\n\n    // Entitlement gate: only users with apiAccess may create API keys.\n    // This is catalog-driven — Pro (tier 1) has apiAccess=false;\n    // API_STARTER+ (tier 2+) have apiAccess=true.\n    const entitlement = await ctx.db\n      .query(\"entitlements\")\n      .withIndex(\"by_userId\", (q) => q.eq(\"userId\", userId))\n      .first();\n    if (\n      !entitlement ||\n      entitlement.validUntil < Date.now() ||\n      !entitlement.features.apiAccess\n    ) {\n      throw new ConvexError(\"API_ACCESS_REQUIRED\");\n    }\n\n    const scopes = normalizeCompanyMonitoringScopes(args.scopes);\n    // Issuing a scoped key is a first-use entry point, so it provisions the\n    // root. Requesting no scopes must stay entirely off Company Monitoring.\n    const companyMonitoringAccount = scopes\n      ? await ensureActiveAccount(ctx, userId, entitlement)\n      : null;\n    if (scopes && !companyMonitoringAccount) {\n      throw new ConvexError(\"COMPANY_MONITORING_ACCESS_DENIED\");\n    }\n\n    if (!args.name.trim()) {\n      throw new ConvexError(\"INVALID_NAME\");\n    }\n    if (!/^wm_[a-f0-9]{5}$/.test(args.keyPrefix)) {\n      throw new ConvexError(\"INVALID_PREFIX\");\n    }","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/koala73/worldmonitor/blob/eeab0a219fce0f02a00603b532dbae9041b934ac/convex/apiKeys.ts#L46-L82","documentation":"Entitlement gate on API-key creation: the user has no entitlement row, their entitlement has expired (validUntil in the past), or the plan's feature catalog does not include apiAccess (Pro tier 1 lacks it; API_STARTER tier 2+ has it). The mutation refuses to issue a key because API access is a paid, catalog-driven feature.","triggerScenarios":"Thrown at convex/apiKeys.ts:64 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Upgrade to an API-enabled plan (API_STARTER or higher) before creating keys","Check the entitlements row (existence, validUntil, features.apiAccess) to see which gate failed","If the entitlement looks wrong, verify the plan catalog's apiAccess flags and re-sync the entitlement"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"eeab0a219fce0f02a00603b532dbae9041b934ac","analyzedAt":"2026-08-21T16:51:25.751Z","contentChangedAt":"2026-08-21T16:51:25.751Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}