CLI First
# Register (new account)
naive register --name "My Agent" --email owner@example.com --password securepassword123 --company "Acme Corp"
# Login (existing account)
naive login --email owner@example.com --password securepassword123
# Passwordless link flow
naive link --email existing@example.com
naive verify --email existing@example.com --code 482901
Browser sign-in (no password)
naive auth runs a browser-based login and drops the resulting API key into your local CLI config — the fastest path for a human (or a coding agent asking a human) to get set up without handling a password. It works for both new and existing accounts.
# Sign in / sign up with Google in your browser
naive auth google
# Sign in / sign up with an email magic link
naive auth email owner@example.com
Both commands start a temporary loopback listener on 127.0.0.1, open your browser to complete authentication, and receive the credential back on the loopback — nothing is pasted or echoed to the terminal. On success the CLI stores your API key and you’re ready to run any command.
naive auth google / naive auth email establish your developer identity and API key. This is different from the Auth primitive, which manages the end-users of a fullstack app you deploy.
Human session (for consent-gated actions)
A few actions are human-only and cannot be performed with an agent API key alone — voice cloning and voice revocation record a legal consent affirmation, so they require a signed-in human session (a cookie), not just a key.
# Establish a human session cookie (browser loopback)
naive auth session-login
# ...now human-gated commands work:
naive voice clone --file sample.wav --name "Founder" --i-affirm
naive voice revoke <voice-id> --yes
# End the session (clears server-side + local cookie)
naive auth logout
Under the hood the browser flow calls GET /v1/auth/oauth/google (Google) or POST /v1/auth/magic/start (email magic link), and the human session is a naive_session cookie validated by the API on consent-gated routes.
API Keys
Every request requires a Bearer token: Authorization: Bearer nv_sk_live_...
Keys are scoped to one agent inside one company. Identity is resolved automatically — you never need to pass agent or company IDs.
Getting a Key
Option A: Self-Register (new account)
POST /v1/auth/register
{
"name": "My Agent",
"email": "owner@example.com",
"password": "securepassword123",
"company_name": "Acme Corp"
}
Returns an API key immediately. Creates a new company with 20 free credits. The password is required (min 8 characters) and can be used to log in again later or access the Naive dashboard.
Option B: Login (existing account)
If you already registered or have a dashboard account:
POST /v1/auth/login
{
"email": "owner@example.com",
"password": "securepassword123"
}
Returns a fresh API key. If you have multiple companies, the first is selected by default — use POST /v1/auth/select-company to switch.
Option C: Link Existing Account (passwordless)
If your owner already has a Naive account and prefers email verification:
Request verification code
POST /v1/auth/link
{ "email": "existing@example.com" }
Verify with code
POST /v1/auth/verify
{ "email": "existing@example.com", "code": "482901" }
Returns API key + list of available companies.Select company (if multiple)
POST /v1/auth/select-company
{ "company_id": "uuid" }
Key Management
# List keys
GET /v1/auth/keys
# Create new key
POST /v1/auth/keys
{ "name": "Production Key" }
# Revoke key (immediate, irreversible)
DELETE /v1/auth/keys/:id
Revoking a key takes effect immediately. Any clients using that key will start receiving 401 errors.
Rate Limiting
| Tier | Rate |
|---|
| Free | 60 requests/minute |
| Pro | 300 requests/minute |
Rate limit headers on every response:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1714250400
Every error follows this structure:
{
"error": {
"code": "unauthorized",
"message": "Missing or invalid Authorization header",
"hint": "Set header: Authorization: Bearer nv_sk_...",
"docs": "https://docs.usenaive.ai/getting-started/authentication"
}
}
Error Codes
| Code | Status | Meaning |
|---|
unauthorized | 401 | Missing or invalid Bearer token |
forbidden | 403 | Key valid but not authorized for this resource |
insufficient_credits | 402 | Not enough credits |
rate_limited | 429 | Too many requests |
invalid_inbox | 400 | Inbox UUID not found or not owned |
resource_not_found | 404 | Entity doesn’t exist |
invalid_input | 400 | Validation failure |
provider_error | 502 | External service failed |
duplicate_request | 409 | Idempotency-Key already used |
duplicate_record | 409 | Record already exists (e.g., email taken) |
Idempotency
For mutation requests, pass an Idempotency-Key header to prevent duplicate operations:
curl -X POST https://api.usenaive.ai/v1/email/send \
-H "Authorization: Bearer nv_sk_live_..." \
-H "Idempotency-Key: unique-request-id-123" \
-H "Content-Type: application/json" \
-d '{ ... }'
If the same key is seen within 24 hours, the original response is returned without re-executing.