account.view audit entry per session and account per 15 minutes (no reason needed for the detail).| Location | Name | Required | Meaning |
|---|---|---|---|
| path | sub | Yes | Use the ID from an authorized staff response; source validators reject malformed IDs. |
account.view audit entry per session and account per 15 minutes (no reason needed for the detail).{error:{code,message}} for CloudError failures, attaches Request-Id, and disables caching. Handle 401 by signing in; show role refusal for 403 forbidden, reauthenticate only for step_up_required, and collect a reason for 400 reason_required. A CSRF failure needs a current page/session token, not blind retries. Operation-specific 404/409/413/422/502/503 errors retain their status. Honor Retry-After on 429./**
* GET /api/admin/accounts/:sub (accounts.read, ADMIN.md §8): the profile (full email and name), sign-in events when
* the pool records them, the deletion hold, Handwrite Cloud counts, 30 days of usage and the Voices plan/credits.
* Writes one `account.view` audit entry per session and account per 15 minutes (no reason needed for the detail).
*
* PATCH (name / email changes, accounts.update with step-up) is added to this same file by package 7 (account-actions).
*/
export const GET = adminRoute("accounts.read", async (_request, ctx, { params }: Context) => {
const { sub } = await params;
if (!isSub(sub)) throw notFound("Account");
const detail = await accountDetail(sub);
if (!detail) throw notFound("Account");
await recordAccountView(ctx, detail.profile.sub);
return adminJson(detail);
});