Skip to content

Users & Auth API

Manage user authentication, profiles, roles, and permissions.

Before diving into the raw API, check out the Authentication Integration Guide for complete flows:

  • Registration & Login: Email/password and OAuth (Google)
  • Token Management: Guest, authenticated, and server tokens
  • Protected Routes: SSR and CSR auth patterns
  • Profile Management: Update user info with correct field names
import { createArkySDK } from 'arky-sdk';
const sdk = createArkySDK({
baseUrl: 'https://api.arky.io',
businessId: 'your-business-id',
autoGuest: true,
getToken: () => JSON.parse(localStorage.getItem('arky_token') || 'null'),
setToken: (token) => localStorage.setItem('arky_token', JSON.stringify(token)),
logout: () => localStorage.removeItem('arky_token'),
});
// Register
const { user, token } = await sdk.user.register({
password: 'SecurePass123!',
firstName: 'John',
lastName: 'Doe',
});
// Get profile
const me = await sdk.user.me();
// Update profile (note: phoneNumbers is an array)
await sdk.user.updateUser({
id: me.id,
firstName: 'Jane',
phoneNumbers: ['+1234567890'], // Array, not singular phoneNumber
});

See Authentication Guide for complete patterns.


All User endpoints are prefixed with /v1/users.

Arky supports multiple authentication providers:

  • EMAIL: Email + password with confirmation flow
  • GOOGLE: OAuth2 via Google
  • GUEST: Phone number-based guest tokens
  • API: API keys for server-to-server

Create a new user account with email and password.

Endpoint: POST /v1/users/register

const result = await sdk.user.registerUser({
provider: 'EMAIL_REGISTER',
password: 'SecureP@ssw0rd',
reserveDomain: 'https://myapp.com', // For confirmation email link
});
// Confirmation email sent to user

Response:

{
"success": true
}

Process:

  1. Password is hashed with SHA256
  2. User created with is_confirmed: false
  3. Confirmation email sent with JWT token (valid 1 hour)
  4. User must click link to activate account

Errors:

  • 400: Email already exists
  • 422: Validation failed (weak password, invalid email)

Activate user account after registration.

Endpoint: PUT /v1/users/confirm

await sdk.user.confirmUser({
token: 'JWT_TOKEN_FROM_EMAIL',
});
// Account now active, user can login

Endpoint: POST /v1/users/login

const auth = await sdk.user.loginUser({
provider: 'EMAIL',
password: 'SecureP@ssw0rd',
});
console.log('Access token:', auth.access_token);
console.log('Refresh token:', auth.refresh_token);
console.log('Expires at:', auth.expires_at);

Response:

{
"userId": "user_abc123",
"provider": "EMAIL",
"accessToken": "eyJhbGc...",
"refreshToken": "eyJhbGc...",
"tokenType": "Token",
"expiresAt": 1698765432,
"scope": "Fake",
"isGuest": false
}

Errors:

  • 422: Invalid email or password
  • 422: Email not confirmed
  • 400: Validation failed

Step 1: Get Login URL

Endpoint: GET /v1/users/login/url

const { url } = await sdk.user.getLoginUrl({
provider: 'GOOGLE',
originUrl: 'https://myapp.com',
redirectUrl: 'https://myapp.com/auth/callback',
});
// Redirect user to Google login
window.location.href = url;

Step 2: Exchange Code for Tokens

After Google redirects back with code:

const auth = await sdk.user.loginUser({
provider: 'GOOGLE',
code: 'GOOGLE_AUTH_CODE',
originUrl: 'https://myapp.com',
});

Note: If the user’s email doesn’t exist, a new user is automatically created with is_confirmed: true.

Create a guest account using phone number.

const auth = await sdk.user.loginUser({
provider: 'GUEST',
phoneNumber: '+1234567890',
});
// Returns token with is_guest: true

Obtain a new access token using a refresh token.

Endpoint: POST /v1/users/refresh (inferred)

// SDK handles refresh automatically, but you can manually call:
const newAuth = await sdk.user.refreshAccessToken({
provider: 'GOOGLE',
refreshToken: 'CURRENT_REFRESH_TOKEN',
});

Note: Refresh tokens for EMAIL and GOOGLE have no expiry (exp: None). Access tokens expire in 3600 seconds (1 hour).


Revoke OAuth tokens (Google only).

Endpoint: POST /v1/users/logout

await sdk.user.logout({
provider: 'GOOGLE',
token: 'ACCESS_TOKEN',
originUrl: 'https://myapp.com',
});

Fetch authenticated user profile.

Endpoint: GET /v1/users/me

Headers: Authorization: Bearer <access_token>

const user = await sdk.user.getMe({});
console.log('User ID:', user.id);
console.log('Email:', user.email);
console.log('Roles:', user.roles);
console.log('Business configs:', user.business_user_configs);

Response:

{
"id": "user_abc123",
"name": "John Doe",
"email": "[email protected]",
"isConfirmed": true,
"phoneNumbers": ["+1234567890"],
"addresses": [],
"roleIds": ["role_xyz"],
"roles": [
{
"id": "role_xyz",
"name": "Admin",
"permissions": [...]
}
],
"apiTokens": [
{
"id": "token_123",
"name": "My API Key",
"value": "••••••••",
"provider": "API"
}
],
"businessUserConfigs": [
{
"businessId": "biz_123",
"settings": {}
}
],
"lifecycle": {
"lastLoginAt": 1698765432,
"onboardingCompleted": true
}
}

Endpoint: PUT /v1/users/update

Headers: Authorization: Bearer <access_token>

const updated = await sdk.user.updateUser({
name: 'Jane Smith',
phoneNumbers: ['+1234567890'], // Array of phone numbers
addresses: [
{
street: '123 Main St',
city: 'New York',
state: 'NY',
zip: '10001',
country: 'US',
},
],
});

Endpoint: POST /v1/users/phone-number/confirm

await sdk.user.phoneNumberConfirm({
phoneNumber: '+1234567890',
code: '123456',
});

Send password reset email.

Endpoint: POST /v1/users/forgot-password

await sdk.user.forgotPassword({
reserveDomain: 'https://myapp.com',
});
// Reset link sent to email

Endpoint: POST /v1/users/reset-forgot-password

await sdk.user.resetForgotPassword({
token: 'RESET_TOKEN_FROM_EMAIL',
password: 'NewSecureP@ssw0rd',
});

Endpoint: POST /v1/users/reset-password

Headers: Authorization: Bearer <access_token>

await sdk.user.resetPassword({
oldPassword: 'OldP@ssw0rd',
newPassword: 'NewSecureP@ssw0rd',
});

Search users within a business (requires ADMIN permission).

Endpoint: GET /v1/users/search

Headers: Authorization: Bearer <access_token>

const { items, cursor } = await sdk.user.searchUsers({
query: 'john',
businessId: 'biz_123',
roleIds: ['role_admin'],
limit: 20,
});

Assign a role to a user (requires ADMIN permission on business).

Endpoint: PUT /v1/users/set-role

Headers: Authorization: Bearer <access_token>

await sdk.user.setRole({
userId: 'user_abc123',
roleId: 'role_manager',
businessId: 'biz_123',
});

Users can generate API tokens for server-to-server auth.

Generating a Token:

  1. Call updateUser with apiTokens array:
await sdk.user.updateUser({
name: user.name,
apiTokens: [
{
id: 'token_new',
name: 'Production API Key',
value: 'arky_live_...', // Generate securely
provider: 'API',
},
],
});

Using API Tokens:

Terminal window
curl -H "X-API-Key: arky_live_..." \
https://api.arky.io/v1/businesses/YOUR_BUSINESS_ID

CodeErrorDescription
400BAD_REQUESTInvalid provider or malformed request
401UNAUTHENTICATEDMissing or invalid token
403FORBIDDENInsufficient permissions
404NOT_FOUNDUser not found or email not confirmed
422VALIDATION_FAILEDField validation errors
{
"error": "VALIDATION_FAILED",
"message": "Validation errors occurred",
"details": [
{
"field": "email",
"error": "EMAIL_EXISTS",
"message": "email exists"
}
]
}
  • EMAIL_EXISTS: Email already registered
  • PASSWORD_WRONG: Invalid credentials
  • PASSWORD_REQUIRED: Password field missing
  • NOT_CONFIRMED: Email not verified
  • INVALID_ORIGIN_URI: OAuth origin not whitelisted
  • INVALID_REDIRECT_URI: OAuth redirect not whitelisted

Access Token Claims:

{
sub: "user_abc123", // User ID
email: "[email protected]", // Email (optional for guests)
provider: "EMAIL", // AUTH provider
exp: 1698765432 // Expiry timestamp
}

Refresh Token Claims:

Same as access token, but exp is null (never expires).