Documentation
UnityAuth API
UnityAuth is the identity provider for every UNX app. It handles accounts, email verification, sign-in, password reset, sessions, roles, tenants and the audit log. This page documents everything live on https://auth.unx.ng today.
Core concepts
- Sessions are cookies
- A successful sign-in sets an HttpOnly, Secure session cookie. Browsers send it automatically with credentials: "include". Never copy session tokens into localStorage.
- Trusted origins
- Requests that change state must carry an Origin header from an allowed site (UNX apps on *.unx.ng). Ask the UNX team to add your app's origin.
- Verified email
- Email and password accounts must verify their email with a 6-digit code before the password works. Passwordless and social sign-ins arrive verified.
- Stable ids
- Ids are prefixed and time-sortable: usr_ users, ses_ sessions, org_ tenants, aud_ audit events. Link your data to them.
API reference
Badges show what each call needs: Public no session, Signed in a session cookie, permission a role that grants that permission.
Sign up and email verification
Email and password sign-up creates the account and emails a 6-digit code. The account cannot sign in with its password until the email is verified; verifying signs the user in.
/v1/auth/sign-up/emailPublicCreate an account and email a verification code.
{
"name": "Ada Obi",
"email": "ada@example.com",
"password": "at-least-8-chars",
"username": "adaobi" // optional; generated when omitted
}{ "token": null, "user": { "id": "usr_…", "email": "ada@example.com", "emailVerified": false, "username": "adaobi" } }/v1/auth/email-otp/verify-emailPublicVerify the email with the 6-digit code. Sets the session cookie.
- Codes expire after 5 minutes and allow 3 attempts.
{ "email": "ada@example.com", "otp": "123456" }{ "status": true, "token": "…", "user": { "emailVerified": true, … } }/v1/auth/email-otp/send-verification-otpPublicSend a fresh code (email-verification or sign-in).
{ "email": "ada@example.com", "type": "email-verification" } // or "sign-in"{ "success": true }Sign in
Every method resolves to the same UNX user. A successful sign-in sets an HttpOnly session cookie; there is no token for the browser to store.
/v1/auth/sign-in/emailPublicEmail and password.
- 401 INVALID_EMAIL_OR_PASSWORD for wrong credentials.
- 403 EMAIL_NOT_VERIFIED when the email is unverified; a fresh verification code is emailed automatically.
{ "email": "ada@example.com", "password": "…", "rememberMe": true }{ "redirect": false, "token": "…", "user": { … } }/v1/auth/sign-in/usernamePublicUsername and password.
{ "username": "adaobi", "password": "…", "rememberMe": true }/v1/auth/sign-in/email-otpPublicPasswordless: sign in with a code sent by send-verification-otp (type sign-in).
- Creates the account on first use, already verified.
{ "email": "ada@example.com", "otp": "123456" }/v1/auth/sign-in/socialPublicStart Google, Microsoft, Apple, GitHub, Facebook, LinkedIn or X sign-in.
- Only providers listed by GET /v1/providers are enabled.
{ "provider": "google", "callbackURL": "https://your.app/after-login", "errorCallbackURL": "https://your.app/login" }{ "url": "https://accounts.google.com/…", "redirect": true }/v1/providersPublicSocial providers that are switched on.
{ "providers": ["google"] }/v1/auth/get-sessionPublicThe current user and session, or null.
{ "user": { … }, "session": { "id": "ses_…", "expiresAt": "…", "location": "Abuja, Nigeria" } }/v1/auth/sign-outSigned inEnd the current session.
{}Password reset
One email carries two ways to reset: a link (valid 60 minutes) and a 6-digit code (valid 5 minutes). Using either cancels the other, and a reset signs the account out on every device. Unknown emails get the same response and no email.
/v1/auth/email-otp/request-password-resetPublicEmail a reset link and a reset code.
{ "email": "ada@example.com" }{ "success": true }/v1/auth/email-otp/reset-passwordPublicReset with the emailed code.
{ "email": "ada@example.com", "otp": "123456", "password": "new-password" }{ "success": true }/v1/auth/reset-password/{token}?callbackURL=…PublicThe emailed link. Validates the token and redirects to the reset page with ?token=… (or ?error=INVALID_TOKEN).
/v1/auth/reset-passwordPublicReset with the token from the link.
{ "token": "…", "newPassword": "new-password" }{ "status": true }Account, sessions and devices
Endpoints for the signed-in user. Each session records its device, IP address and an approximate location.
/v1/me/permissionsSigned inThe user's role(s) and permission keys.
{ "role": "user", "roles": ["user"], "permissions": [] }/v1/me/tenantsSigned inOrganisations the user belongs to.
{ "items": [{ "role": "member", "tenant": { "tid": "unityx", "name": "UnityHUB", … } }] }/v1/auth/update-userSigned inUpdate the display name or image.
{ "name": "Ada O." }/v1/auth/change-passwordSigned inChange password.
{ "currentPassword": "…", "newPassword": "…", "revokeOtherSessions": true }/v1/auth/list-sessionsSigned inActive sessions with device, IP and location.
/v1/auth/revoke-sessionSigned inSign one device out.
{ "token": "<session token from list-sessions>" }/v1/auth/revoke-other-sessionsSigned inSign out every other device.
{}/v1/auth/list-accountsSigned inLinked sign-in methods (credential, google, …).
/v1/auth/link-socialSigned inLink a social provider to the signed-in account. Redirect the browser to the returned url.
{ "provider": "google", "callbackURL": "https://your.app/account", "errorCallbackURL": "https://your.app/account" }{ "url": "https://accounts.google.com/…", "redirect": true }/v1/auth/unlink-accountSigned inUnlink a provider (not the last sign-in method).
{ "accountId": "acc_… (id from list-accounts)" }Tenants and API keys
Create and manage organisations (tenants) and their API keys, as in the developer console. Owners and admins of a tenant can edit it and manage keys; only the owner can delete it. The default tenant (UnityHUB, unityx) cannot be deleted. Writes must come from a trusted Origin.
/v1/me/tenantsSigned inTenants you belong to, with your role.
/v1/tenantsSigned inCreate a tenant. You become its owner. Requires a verified email; up to 10 per user.
- 409 tid_taken or tid_reserved; 400 invalid_tid.
{
"name": "Acme Ventures",
"tid": "acme-ventures", // optional, permanent; derived from name if omitted
"description": "…",
"url": "https://acme.example"
}{ "id": "org_…", "tid": "acme-ventures", "name": "Acme Ventures", "status": "active", … } // 201/v1/tenants/{id}Signed inA tenant with your role and counts.
{ "id": "org_…", "tid": "…", "myRole": "owner", "isDefault": false, "counts": { "members": 1, "apiKeys": 0 } }/v1/tenants/{id}Signed inUpdate name, description, url, contactEmail, contactPhone or logoUrl (owner or admin).
{ "name": "Acme Ventures Ltd", "url": "https://acme.example" }/v1/tenants/{id}Signed inDelete a tenant with its memberships and API keys (owner only). Answers 204.
/v1/tenants/{id}/membersSigned inMembers and their roles.
/v1/tenants/{id}/api-keysSigned inAPI keys (name, prefix, dates). Secrets are never returned.
/v1/tenants/{id}/api-keysSigned inCreate an API key. The full key is in this response only; store it immediately.
{ "name": "Production server", "expiresInDays": 365 } // expiresInDays optional{ "key": "unx_live_…", "apiKey": { "id": "key_…", "prefix": "unx_live_ab12cd", … } } // 201/v1/tenants/{id}/api-keys/{keyId}Signed inRevoke a key immediately.
/v1/integration/whoamiPublicFor integrations: authenticate with a tenant API key and get the tenant it acts for.
- Send the key as `Authorization: Bearer unx_live_…` or `X-API-Key: unx_live_…`. Revoked, expired or unknown keys get 401.
{ "tenant": { "id": "org_…", "tid": "acme-ventures", "name": "Acme Ventures", "status": "active" }, "apiKey": { "id": "key_…", "name": "…", "prefix": "…" } }Admin
User management for support, admin and super-admin roles. Only a super-admin can grant or remove admin roles or act on an admin account, and the last super-admin can never be demoted. Every action is audited, including refused ones.
/v1/auth/admin/list-users?searchValue=&limit=25&offset=0user:listSearch and page through users.
{ "users": [ … ], "total": 42 }/v1/auth/admin/set-roleuser:set-roleChange a user's role.
{ "userId": "usr_…", "role": "support" }/v1/auth/admin/ban-useruser:banBan a user and end their sessions.
{ "userId": "usr_…", "banReason": "spam" }/v1/auth/admin/unban-useruser:banLift a ban.
{ "userId": "usr_…" }/v1/auth/admin/revoke-user-sessionssession:revokeSign a user out everywhere.
{ "userId": "usr_…" }/v1/auth/admin/remove-useruser:deleteDelete a user.
{ "userId": "usr_…" }/v1/admin/audit-logs?action=&actorUserId=&targetId=&before=&limit=50audit:readAudit log, newest first. Page with nextCursor → before.
{ "items": [{ "id": "aud_…", "action": "admin.role_change.success", … }], "nextCursor": "aud_…" }Roles and permissions
Permissions are resource:action keys. Read the current user's with GET /v1/me/permissions. No role can impersonate users.
| Role | Who it is for | Permissions |
|---|---|---|
| user | Default for every account. Manages their own profile, sign-in methods and devices. | None beyond their own account |
| support | Looks up users and sessions, reads the audit log, views tenants. | user:listuser:getsession:listaudit:readtenant:read |
| admin | Manages users, sessions and tenants. | user:createuser:listuser:getuser:updateuser:set-roleuser:banuser:deleteuser:set-passworduser:set-emailsession:listsession:revokesession:deleteaudit:readtenant:readtenant:manage |
| super-admin | Everything admin can, plus granting admin roles and managing admin accounts. | All admin permissions |
Tenants
A tenant is an organisation with an owner and members. The default tenant is UnityHUB (tid unityx): every UNX account joins it as a member when it is created, so apps can rely on at least one membership per user.
{
"items": [
{
"role": "member",
"joinedAt": "2026-09-26T08:00:00.000Z",
"tenant": { "id": "org_…", "tid": "unityx", "slug": "unityx", "name": "UnityHUB", "status": "active" }
}
]
}Errors and limits
Authentication routes return { "code", "message" }. Platform routes under /v1 return { "error": { "code", "message", "requestId", "details" } }; quote requestId (also in the X-Request-Id header) when reporting a problem.
| Status | Code | Meaning |
|---|---|---|
| 400 | BAD_REQUEST / invalid_query | The body or query failed validation. |
| 401 | UNAUTHORIZED / INVALID_EMAIL_OR_PASSWORD | No session, or wrong credentials. |
| 403 | EMAIL_NOT_VERIFIED / forbidden | Verify the email first, or the role lacks the permission. |
| 404 | not_found | Unknown route. |
| 429 | TOO_MANY_REQUESTS | Rate limit reached: 100 requests a minute per IP, 3 code emails a minute. |
Social sign-in
Google, Microsoft, Apple, GitHub, Facebook, LinkedIn and X are supported. A provider is live once its OAuth client is configured; GET /v1/providers lists those that are. The callback URL registered with each provider is:
Account linking. When a social sign-in uses the same email as an existing UNX account, it links to that account automatically, but only if the provider confirms the email is verified and the UNX account's email is verified. Otherwise the user is sent back with ?error=account_not_linked. Signed-in users can also link or unlink providers with POST /v1/auth/link-social and POST /v1/auth/unlink-account.