Verification flow
POST /v1/users (bootstrap) creates the user, returns a restricted mk_user_* key, and emails the user a 6-digit code. The agent then submits the code to POST /v1/users/:userId/verify — the same key is upgraded in place to full scope. No key rotation; store the value once.
Step 1 — POST /v1/users
Required scope: developer:bootstrap. Returns 201 Created (or 207 Multi-Status if a starter manifest had over-cap products):
sourceAgent is required (1–64 chars, [A-Za-z0-9 _.-]+ only). It is embedded in the verification email so the user can see which agent triggered the account.
Response:
userKey immediately. It is shown once. It starts with the restricted scope set:
Until verify, the restricted key cannot create/update catalog data or publish.
Step 2 — submit the code
me:verify. The :userId path param must match the key’s owner — cross-tenant or wrong-id attempts return 404 user_not_found (leak-less, never 403).
On success (200):
catalog:read, catalog:write, storefront:publish. Cache propagation is bounded by the 30s positive auth cache; the planLimits cache for the user is invalidated immediately so GET /v1/me reflects verified on the next call.
Code rules
Plain-text storage is intentional. The user-reads-aloud flow requires the agent to be able to say the code on screen. A bcrypt-hashed code would only support submit-then-check, not the agentic read-aloud variant. Defense-in-depth lives elsewhere: 3-attempts lockout, 15-min TTL, per-user resend rate-limit, and the leak-less 404 on cross-tenant verify.
Verify error shapes
Step 3 — resend (when needed)
me:resendVerification. Per-user rate-limited at 3 / hour and 5 / day. Overwrites the existing code doc — the old code is invalidated on resend.
Returns:
Variants of step 2
A — User reads the code aloud. Default. Agent says “I sent you a 6-digit code; tell me the number.” User reads from email. Agent submits. B — Silent verification via Gmail MCP. If the user’s mailbox is connected to the agent (e.g. Gmail MCP), the agent reads the verification email programmatically, extracts the 6-digit code, and submits — no manual step. The API contract is identical; only the source of the code differs.Account-cancellation hatch
Every bootstrap email also embeds a single-use cancel link (24h TTL) pointing athttps://api.mareaalcalina.com/public/v1/bootstrap/:previewToken. The user opens it, confirms once (GET → POST, two-step to defeat email-preview crawlers), and the account is hard-deleted. This is the LFPDPPP-required cancellation hatch for accounts created by an agent (see ARCO procedures).
Verification in code
src/api/v1/users.bootstrap.tssrc/api/v1/users.verify.tssrc/api/v1/users.resendVerification.tssrc/api/services/verification.service.ts— 3-attempt lockout + scope upgrade.src/services/verification/sendVerificationCodeCore.ts— code generation + email delivery (15-min API TTL).src/api/public/bootstrap.cancel.ts— emergency cancel endpoint.