Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.mareaalcalina.com/llms.txt

Use this file to discover all available pages before exploring further.

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.
Agent (dev key)                      User
  │                                    │
  │ POST /v1/users                     │
  ├─────────────────────────▶          │
  │  { email, displayName,             │
  │    sourceAgent: "..." }            │
  │                                    │
  │ 201 + restricted user key          │
  ◀─────────────────────────┤          │
  │  + verificationExpiresAt           │
  │                                    │
  │                  email with 6-digit code (15 min TTL)
  │                                    │
  │                                    │  reads code aloud OR
  │                                    │  agent reads it from Gmail MCP
  │                                    │
  │ POST /v1/users/:userId/verify      │
  ├─────────────────────────▶          │
  │  { code: "123456" }                │
  │                                    │
  │ 200 { verificationStatus:          │
  ◀─────────────────────────┤          │
  │      "verified" }                  │
  │                                    │
  │ same key — now has                 │
  │ catalog:write + storefront:publish │

Step 1 — POST /v1/users

Required scope: developer:bootstrap. Returns 201 Created (or 207 Multi-Status if a starter manifest had over-cap products):
POST /v1/users HTTP/1.1
Authorization: Bearer mk_dev_...
Content-Type: application/json
Idempotency-Key: 2f1a8c4b-2e3a-4b9d-9f1a-8c4b2e3a4b9d

{
  "email": "owner@taqueria.example",
  "displayName": "La Taquería",
  "country": "MX",
  "language": "es",
  "currency": "MXN",
  "businessType": "restaurante",
  "sourceAgent": "claude-code",
  "initialStorefront": { "name": "Tacos La Marea", "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:
{
  "userId": "usr_...",
  "storefrontId": "stf_...",
  "userKey": "mk_user_...",
  "verificationStatus": "pending",
  "verificationExpiresAt": "2026-05-10T20:15:00Z",
  "verificationDeliveryHint": "email-only",
  "previewToken": "...",
  "appliedDefaults": { "language": "es", "currency": "MXN", "country": "MX", "businessType": "restaurante" },
  "idempotent": false
}
Store userKey immediately. It is shown once. It starts with the restricted scope set:
ScopePurpose
catalog:readInspect the draft storefront
me:verifySubmit the 6-digit code
me:resendVerificationRequest a fresh code if the email was lost
Until verify, the restricted key cannot create/update catalog data or publish.

Step 2 — submit the code

POST /v1/users/:userId/verify HTTP/1.1
Authorization: Bearer mk_user_...
Content-Type: application/json

{ "code": "123456" }
Required scope: 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):
{ "userId": "usr_...", "verificationStatus": "verified" }
The same key’s scope set is upgraded in place to 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

PropertyValue
Format6 decimal digits (^\d{6}$)
TTL15 minutes from issuance
Max attempts3 wrong attempts → 429 too_many_attempts (must resend)
StorageStored in plain text (intentional — see below)
Source of randomnesscrypto.randomInt (CSPRNG)
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

Statuserror.codeMeaningAgent action
400code_invalidThe submitted code doesn’t matchRe-prompt the user; on 3rd wrong attempt, resend
404code_not_foundNo active code for this user (never sent, or fully consumed)Call POST /v1/users/:userId/resendVerification
404user_not_found:userId doesn’t match the key’s owner (silent denial)Use the correct user key
410code_expiredCode older than 15 minResend
429too_many_attempts3 wrong attempts in this verification windowResend

Step 3 — resend (when needed)

POST /v1/users/:userId/resendVerification HTTP/1.1
Authorization: Bearer mk_user_...
Required scope: 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:
{ "verificationStatus": "pending", "verificationExpiresAt": "2026-05-10T20:30:00Z" }
Statuserror.codeMeaning
429resend_hour_limit3 resends used in the current hour
429resend_day_limit5 resends used today

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 at https://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.ts
  • src/api/v1/users.verify.ts
  • src/api/v1/users.resendVerification.ts
  • src/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.