Skip to main content

Quickstart

From an empty database to an authenticated API call. The examples use nextneural_converse; the same five steps work on any of the four binaries — only the last one differs.

Prerequisites

A running binary and a reachable Postgres. See Running a binary if nothing is up yet.

1. Create an account​

Signup is two steps: submit your details, then confirm the six-digit code that lands in your inbox. The account does not exist until the code is confirmed.

curl -X POST https://your-host/api/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "a-strong-password",
"first_name": "Priya",
"last_name": "Kumar"
}'
{ "detail": "We've emailed you a code — enter it to finish creating your account." }

Then confirm it:

curl -X POST https://your-host/api/auth/verify-signup-otp \
-H "Content-Type: application/json" \
-d '{ "email": "[email protected]", "code": "428913" }'

This returns 201 with your session token and user record:

{
"token": "eyJhbGciOiJIUzI1NiIs...",
"user": {
"id": 1,
"uuid": "dac0309e-d46d-4b48-a420-05d976e64c5c",
"email": "[email protected]",
"role": "admin",
"mobile_number": null,
"phone_number": null,
"profile_picture": null,
"organization": null,
"must_change_password": false,
"has_password": true,
"superadmin": false
}
}

"organization": null means setup is not finished. That is the next step.

2. Create your organization​

curl -X POST https://your-host/api/organization \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Pragati AI",
"website": "https://pragati.example",
"employee_count": "11-50",
"company_type": "generic_sales"
}'

The account that creates an organization becomes its admin.

One organization per deployment, by default

DEPLOYMENT_MODE defaults to sovereign, which permits exactly one organization. Once it exists, this call fails for everyone else:

{ "detail": "this deployment is licensed for a single organization" }   // 403

Set DEPLOYMENT_MODE=cloud to lift the cap — that exact value, nothing else. A client can read the current setting without authenticating:

curl https://your-host/api/auth/config
{ "deployment_mode": "sovereign", "firebase_enabled": false }

An account that already belongs to an organization gets 409 {"detail": "this account already belongs to an organization"} instead, since that check runs first.

3. Get past the waitlist gate​

If your email domain was not on ALLOWED_EMAIL_DOMAINS when you signed up, your organization is created with waitlist_status = "pending" and every org-scoped route returns 403:

{ "detail": "your organization is awaiting approval", "needs_waitlist_approval": true }

There is no API for approving it — this is a deliberate out-of-band admin action:

UPDATE organizations SET waitlist_status = 'approved' WHERE id = 1;

Approval is a property of the organization, not the user, so one update clears every member. Details in Organizations.

4. Log in​

Once approved, exchange your credentials for a session token whenever you need a fresh one:

curl -X POST https://your-host/api/auth/login \
-H "Content-Type: application/json" \
-d '{ "email": "[email protected]", "password": "a-strong-password" }'
{ "token": "eyJhbGciOiJIUzI1NiIs...", "user": { "..." : "..." } }

Use it as Authorization: Bearer <token> on every console endpoint.

5. Create an API key​

Session tokens expire. For server-to-server integration, issue a scoped API key instead — this requires the admin role:

curl -X POST https://your-host/api/api-keys \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "crm-integration",
"scopes": ["calls:read", "customers:read", "customers:write"]
}'
{
"uuid": "6f1a...",
"name": "crm-integration",
"key": "nn_k_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"key_prefix": "nn_k_xxxx",
"detail": "Copy this key now — it will not be shown again."
}

The plaintext key is returned once and never stored. Lose it and you issue a new one.

6. Call the developer API​

curl https://your-host/v1/calls?limit=10 \
-H "Authorization: Bearer nn_k_your_key_here"
{
"data": [
{
"uuid": "c8f2a1b3-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"call_type": "outbound",
"from_number": "+915550010000",
"to_number": "+918888888888",
"duration": 113,
"outcome": "completed",
"started_at": "2026-09-18T14:32:01Z"
}
],
"total": 1
}

The developer API wraps list results in data. The console routes use a named key instead — GET /api/voice/calls returns calls. See the developer API reference.

Where each binary's developer API lives​

BinaryDeveloper API baseNotes
nextneural_converse/v1Calls, contacts, campaigns →
nextneural_intel/api/v1/salesAnalyses and field labels →
nextneural_assist—Scoped keys work on the console routes themselves
nextneural_coach/api/nn-coachSame routes as the console; role-gated ones refuse keys

Next: Authentication in full →