Account context and settings
GET /api/v1/me accepts either an owner session or Publishing key and returns {credential,profile,readiness,actions,limits}. Owner sessions additionally receive {account:{id,email,pendingEmail}} and the keys/finance/settings action URLs. Publishing-key responses omit private email and finance and never expose the full receiving address.
credential.type is owner_session or publishing_key; expiry is ISO time or null, and Publishing credentials include scope: "posts:publish". Readiness has canPublish and {code,field?,action} blockers. profile-incomplete identifies name/handle setup; payout-address-required links to the owner address action. Limits include five active keys, 2 MiB images, 250-character bio, 80-character location, two social links, price min/max/precision and the supported language values. Fix blockers through the appropriate credential family.
Change and confirm email
Email is optional. A wallet owner can use all ordinary account and publication flows without it. Adding or confirming email preserves the original UUID and the current wallet session; Nano login remains available after verification.
These owner-only calls check that the exact session is still active. Publishing keys cannot call them. Responses and confirmation material are private and must never enter logs or caches.
| Method and path | Body | Persisted result |
|---|---|---|
GET /api/v1/account/email | None | email, pendingEmail, status (not_configured, pending or verified) |
POST /api/v1/account/email | {email} | status (confirmation_sent or already_pending), pendingEmail |
POST /api/v1/account/email/resend | None | status:resent, pendingEmail |
POST /api/v1/account/email/verify | {email,code} or {email,confirmationUrl} | Current email/status; email sessions may also receive a renewable token pair |
The request normalizes email to lowercase and trims whitespace; it supports at most 320 characters and a 64-character local part. Repeating the current/pending email returns already_pending. Read GET after response loss. Resend without a pending change returns 400 data.code: "no_pending_email". Invalid/in-use email errors use 400 and data.code: "invalid_email" or "email_in_use".
Verification requires exactly one six-to-eight-digit code or the complete email-change confirmation URL from the authorized mailbox. It must match the configured Supabase origin, /auth/v1/verify, type=email_change, and this owner's pending change. The server checks the token without exposing it or following redirects. Wrong/expired/foreign confirmation returns 422; other URLs are rejected. Provider double-confirmation may leave status pending after the first mailbox confirms; complete each required mailbox confirmation and read status until verified. A lost final success replays as verified for the same final email. For an email session, atomically save any returned accessToken, refreshToken and Unix-seconds expiresAt. Wallet confirmation returns status without replacing the wallet token pair; keep the existing wallet session and its normal refresh lifecycle.
Email request/resend follow the existing provider/abuse policy; production limits include five change attempts/hour and three resends/15 minutes. Verification allows ten attempts/minute. SMTP, templates, CAPTCHA and session policy must be checked before launch. Authentication covers renewing a session independently of email changes.
Linked addresses and privacy
GET /api/v1/nano-addresses with owner Bearer returns only owned rows: {id,address,user_id,created_at,updated_at}. These are read-only linked addresses, distinct from the saved payout address. The ordinary API cannot reassign wallet ownership.
Engagement settings expose existing email/in-app/push preference booleans and creator/supporter leaderboard visibility. OS or browser push permission and administrator/operator controls remain outside ordinary account authority.
Optional legacy email bootstrap
New integrations should use the owner-session email endpoints above after Nano login. The existing /api/v1/account/email/bootstrap family remains a compatibility path for attaching email to a freshly proven wallet without a Bearer header. It is optional; login and publishing are already available before email setup.
All four calls are POSTs with private loginSecret in JSON, without cookies. The continuation lasts 15 minutes after the original claim and retries do not extend it. Start takes {loginSecret,email}; /status and /resend take {loginSecret}; /verify takes {loginSecret,email,code} or {loginSecret,email,confirmationUrl}. The proof must belong to that same account's pending mailbox. Status returns {status,userId,email,pendingEmail,bootstrapExpiresAt} with awaiting_email, pending, confirmation_sent, verified or configuration_blocked. A verified wallet continuation has nextAction:authenticated and returns no replacement pair: keep the wallet pair. Read status after any uncertainty; fresh Nano login recovers the same account after a lost or expired continuation.
EMAIL_CONFLICT (409) cannot reassign another wallet or mailbox. INVALID_CONFIRMATION (422) needs the correct owner's proof. BOOTSTRAP_UNAVAILABLE (503/429) needs bounded retry/status read with the same secret. NANO_BOOTSTRAP_EXPIRED (410/401) needs a new wallet proof. configuration_blocked and EMAIL_CONFIGURATION_BLOCKED are not email completion; provider configuration must be repaired. Pending/configuration-blocked optional email does not remove an otherwise valid wallet session's owner authority.
These calls share 120 requests/minute per ingress IP; each hashed secret/action allows 30/minute, except verification at 10/minute. HTTP 429 can include data.retryAfter seconds. Keep secrets and mailbox proof out of URLs and logs.