Authentication
This guide walks you through authenticating with the Human Dashboard and generating the x-unico-accesskey you will use in all Human Token API requests.
The flow has two phases:
- Authenticate — complete a login and obtain an access token. Two authentication methods are available:
- Biometric login — face capture via the Unico SDK (available for both signup and signin)
- Email OTP login — one-time code sent to the registered email address (signin only)
- Set up your organization — create your company profile and generate an access key (first-time signup only)
Two ways to set up your credentials
Everything in this guide can also be done through the Human Dashboard at dashboard.unico.app — no API calls required.
| Option | When to use |
|---|---|
| Dashboard | First-time setup, manual key rotation, occasional management |
| API | Automated provisioning, CI/CD pipelines, programmatic key management |
Using the dashboard
- Go to dashboard.unico.app
- Sign up or sign in — use biometric login (face capture) or email OTP
- Create your organization — fill in your company name, country, and allowed domains
- Generate an access key — go to Access Keys and click Create key; copy it immediately, it is shown only once
- Manage branches — create and configure branches under Branches; each branch generates its own access key
The access key you get from the dashboard is the same x-unico-accesskey used in all API requests — the two approaches are fully interchangeable.
Base URLs
Every endpoint on this page is served by the Human Dashboard API. The Human Token API requests, such as Create Process and Get Process, go to the API host listed in Environments.
| Environment | API | Base URL |
|---|---|---|
| Production | Human Dashboard API (login, organization, access keys and branches) | https://api.humantoken.unico.app |
Authentication methods
Two methods are available to authenticate and obtain an authToken:
| Method | Endpoint | When to use |
|---|---|---|
| Biometric | POST /api/console/v1/auth/biometric/sessions | Signup (new account) or Signin (returning user) |
| Email OTP | POST /api/console/v1/auth/email/sessions | Signin only — account must already exist |
Biometric login intent
Every call to POST /api/console/v1/auth/biometric/sessions must declare an intent:
intent | When to use | email field | name field |
|---|---|---|---|
LOGIN_INTENT_SIGNUP | First-time registration — creating a new account | Required | Required |
LOGIN_INTENT_SIGNIN | Returning user — account already exists | Not used | Not used |
| (omitted) | Treated as LOGIN_INTENT_SIGNIN | Not used | Not used |
The intent controls the validation done when you poll the session (Step 2):
- SIGNUP — rejects the login if that biometric identity is already registered (prevents duplicate accounts).
- SIGNIN — rejects the login if that biometric identity is not registered (account must exist to log in).
Phase 1 — Authenticate
Step 1: Start a biometric login session
SIGNUP (first-time registration)
curl -X POST https://api.humantoken.unico.app/api/console/v1/auth/biometric/sessions \
-H "Content-Type: application/json" \
-d '{
"intent": "LOGIN_INTENT_SIGNUP",
"email": "[email protected]",
"name": "Jane Doe"
}'
If an account is already registered with that email, the request returns 400 Bad Request.
Response:
{
"session": {
"sessionId": "b3f1a2c4-0000-0000-0000-abcdef012345",
"link": "",
"token": ""
}
}
For SIGNUP, the link and token fields are empty in the response — the biometric capture link is sent to the provided email address.
SIGNIN (returning user)
curl -X POST https://api.humantoken.unico.app/api/console/v1/auth/biometric/sessions \
-H "Content-Type: application/json" \
-d '{
"intent": "LOGIN_INTENT_SIGNIN"
}'
Response:
{
"session": {
"sessionId": "b3f1a2c4-0000-0000-0000-abcdef012345",
"link": "https://humantoken.unico.io/...",
"token": "eyJhbGciOiJSUzI1NiJ9..."
}
}
Save the sessionId.
- SIGNUP: the biometric capture link is sent to the email provided in Step 1. Ask the user to check their inbox and complete the capture.
- SIGNIN: open
linkin your browser and complete the biometric capture.
Step 2: Poll until the session is resolved
curl https://api.humantoken.unico.app/api/console/v1/auth/biometric/sessions/b3f1a2c4-0000-0000-0000-abcdef012345
Poll every 2–3 seconds until status reaches a terminal state.
status | outcome | Meaning |
|---|---|---|
pending | pending | User has not completed the capture yet — keep polling |
processing | pending | Capture done, results being computed — keep polling |
completed | matched | Login successful — authToken is present |
completed | no_match | Biometric did not match any record |
completed | inconclusive | Capture succeeded but could not be resolved |
expired | — | Link expired — restart from Step 1 |
canceled | — | Session was canceled — restart from Step 1 |
Additional intent-specific errors when polling the session:
| HTTP | Condition |
|---|---|
400 | SIGNUP — biometric identity is already registered (duplicate account) |
400 | SIGNIN — biometric identity is not registered (account does not exist) |
Successful response:
{
"result": {
"sessionId": "b3f1a2c4-0000-0000-0000-abcdef012345",
"status": "completed",
"outcome": "matched",
"authToken": "eyJhbGciOiJSUzI1NiJ9..."
}
}
Save authToken — use it as the x-console-authorization header in all subsequent Human Dashboard API requests.
Phase 1 (alternative) — Email OTP signin
Use this flow instead of biometric if the user already has an account and prefers to authenticate via a one-time code sent to their registered email. Signup is not supported via email OTP — use the biometric flow for first-time registration.
Step 1: Send the OTP
curl -X POST https://api.humantoken.unico.app/api/console/v1/auth/email/sessions \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]"
}'
The email must belong to an existing account. If no account is found, Step 2 (validate the OTP) returns 404 Not Found.
Response:
{
"session": {
"sessionId": "b3f1a2c4-0000-0000-0000-abcdef012345"
}
}
Save the sessionId. A 6-digit code is sent to the provided email address.
Step 2: Validate the OTP
curl https://api.humantoken.unico.app/api/console/v1/auth/email/sessions/{sessionId}/{code}
| HTTP | Condition |
|---|---|
200 | Code is valid — authToken is present |
400 | Code is wrong (invalid otp code) |
400 | OTP already used, max attempts reached, or expired |
404 | Email not registered |
Successful response:
{
"result": {
"sessionId": "b3f1a2c4-0000-0000-0000-abcdef012345",
"status": "completed",
"outcome": "matched",
"authToken": "eyJhbGciOiJSUzI1NiJ9..."
}
}
Save authToken — use it as the x-console-authorization header in all subsequent Human Dashboard API requests.
Phase 2 — Set up your organization
Step 3: Create your organization
This call can only be made once. It provisions your tenant and generates your branchId.
Any country is accepted. Use the ISO 3166-1 alpha-3 three-letter code (e.g. BRA, USA, MEX, DEU, IND). taxId is optional for all countries.
curl -X POST https://api.humantoken.unico.app/api/console/v1/organization \
-H "x-console-authorization: <authToken>" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"responsibleEmail": "[email protected]",
"websiteUrl": "https://acme.com",
"countryCode": "BRA",
"logo": "https://acme.com/logo.png",
"allowedDomains": ["https://app.acme.com"]
}'
Response:
{
"organization": {
"id": "e5d3f1a2-0000-0000-0000-abcdef012345",
"name": "Acme Corp",
"websiteUrl": "https://acme.com",
"countryCode": "BRA",
"tenantId": "tenant-acme-prod",
"branchId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"taxId": "12345678000195"
}
}
Save branchId — it identifies your default branch and is used when managing branches and access keys.
Step 4: Create an access key
curl -X POST https://api.humantoken.unico.app/api/console/v1/access-keys \
-H "x-console-authorization: <authToken>" \
-H "Content-Type: application/json" \
-d '{
"label": "production-key"
}'
Response:
{
"accessKey": "uip_ak.prod.a1b2c3d4.e5f6g7h8.i9j0k1l2"
}
The accessKey is returned only once and cannot be retrieved again. Store it in your secrets manager immediately as HUMAN_TOKEN_ACCESS_KEY.
This value is the x-unico-accesskey header used in all Human Token API requests.
What you now have
| Value | Where to use it |
|---|---|
branchId | Identifying your default branch; creating branch-specific access keys |
accessKey | x-unico-accesskey header in all Human Token requests |
Managing your organization
Get organization
curl https://api.humantoken.unico.app/api/console/v1/organization/<organizationId> \
-H "x-console-authorization: <authToken>"
Response:
{
"organization": {
"id": "e5d3f1a2-0000-0000-0000-abcdef012345",
"name": "Acme Corp",
"websiteUrl": "https://acme.com",
"countryCode": "BRA",
"tenantId": "tenant-acme-prod",
"branchId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"taxId": "12345678000195"
}
}
Update organization
curl -X PUT https://api.humantoken.unico.app/api/console/v1/organization \
-H "x-console-authorization: <authToken>" \
-H "Content-Type: application/json" \
-d '{
"id": "e5d3f1a2-0000-0000-0000-abcdef012345",
"name": "Acme Corp",
"responsibleEmail": "[email protected]",
"websiteUrl": "https://acme.com",
"countryCode": "BRA",
"taxId": "12345678000195",
"logo": "https://acme.com/logo-updated.png",
"allowedDomains": ["https://app.acme.com", "https://checkout.acme.com"]
}'
Managing access keys
List keys (metadata only — secret is never returned):
curl https://api.humantoken.unico.app/api/console/v1/access-keys \
-H "x-console-authorization: <authToken>"
Rotate a key (old key is immediately invalidated):
curl -X POST https://api.humantoken.unico.app/api/console/v1/access-keys/rotate \
-H "x-console-authorization: <authToken>" \
-H "Content-Type: application/json" \
-d '{"accessKey": "uip_ak.prod.a1b2c3d4.e5f6g7h8.i9j0k1l2"}'
Revoke a key (irreversible):
curl -X DELETE https://api.humantoken.unico.app/api/console/v1/access-keys \
-H "x-console-authorization: <authToken>" \
-H "Content-Type: application/json" \
-d '{"accessKey": "uip_ak.prod.a1b2c3d4.e5f6g7h8.i9j0k1l2"}'
Managing branches
Branches let you segment your organization's configuration (name, logo, allowed domains) per product or environment. Each branch has its own access keys, so the branch context is resolved automatically from the key used in the request — no need to pass branchId in CreateProcess.
Create a branch
curl -X POST https://api.humantoken.unico.app/api/console/v1/organization/branches \
-H "x-console-authorization: <authToken>" \
-H "Content-Type: application/json" \
-d '{
"name": "Mobile App",
"logo": "https://acme.com/mobile-logo.png",
"allowedDomains": ["https://mobile.acme.com"]
}'
Response:
{
"branchId": "7ab1c2d3-0000-0000-0000-abcdef012345"
}
Save branchId — use it when creating a branch-specific access key.
Update a branch
curl -X PUT https://api.humantoken.unico.app/api/console/v1/organization/branches \
-H "x-console-authorization: <authToken>" \
-H "Content-Type: application/json" \
-d '{
"branchId": "7ab1c2d3-0000-0000-0000-abcdef012345",
"name": "Mobile App v2",
"logo": "https://acme.com/mobile-logo-v2.png",
"allowedDomains": ["https://mobile.acme.com", "https://app.acme.com"]
}'
Create a branch-specific access key
To generate an access key scoped to a specific branch, include branchId in the request body:
curl -X POST https://api.humantoken.unico.app/api/console/v1/access-keys \
-H "x-console-authorization: <authToken>" \
-H "Content-Type: application/json" \
-d '{
"label": "mobile-app-key",
"branchId": "7ab1c2d3-0000-0000-0000-abcdef012345"
}'
Response:
{
"accessKey": "uip_ak.prod.z9y8x7w6.v5u4t3s2.r1q0p9o8"
}
The accessKey is returned only once. Store it immediately. Any CreateProcess request authenticated with this key will automatically use the branch configuration associated with it.