Users
This section contains 1 subgroups.
Get Current User
/users/meRetrieves the current authenticated user. Returns user with warehouse_staff, restaurant_staff (object or null), panel_type, tenant_info, role, notification preferences, market/restaurant portal context flags, and login_country derived from the phone dial code when the user has a phone number. An inactive application user remains inactive across sign-in and other Supabase Auth updates; authentication activity never reactivates an inactive account. Inactive users receive HTTP 403 until an authorized user explicitly changes their application status to active.
Pending invited accounts activate when their confirmed invitation is completed with a first password. Inactive accounts remain inactive.
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code (Optional) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/users/me?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
1{2 "success": true,3 "message": "Users retrieved successfully",4 "user": {5 "id": "user-uuid",6 "email": "[email protected]",7 "first_name": "John",8 "last_name": "Doe",9 "status": "active",10 "notifications_enabled": true,11 "is_system_admin": false,12 "is_staff": true,13 "is_market_user": true,14 "market_id": "market-uuid",15 "is_restaurant_user": false,16 "restaurant_location_id": null,17 "login_country": {18 "code": "IN",19 "name": "India",20 "dial_code": "+91"21 },22 "role": {23 "id": "role-uuid",24 "name": "restaurant_staff",25 "scope": "tenant"26 },27 "panel_type": "dispatcher",28 "warehouse_staff": [],29 "restaurant_staff": {30 "id": "staff-uuid",31 "location_id": "location-uuid",32 "staff_role": "cashier",33 "location": {34 "id": "location-uuid",35 "name": {},36 "code": 1000,37 "tenant_id": "tenant-uuid",38 "active": true39 }40 }41 }42}Update My Context
/users/me/contextStores the authenticated user's market or restaurant context after portal login.
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code (Optional) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
portal | string |
|
market_id | string | required when |
restaurant_location_id | string | required when |
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/users/me/context?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "portal": "market",6 "market_id": "{{market_id}}"7}'Replace {{placeholders}} with your own values before sending.
Response examples
1{2 "success": true,3 "message": "User Updated Successfully",4 "data": {5 "id": "user-uuid",6 "is_market_user": true,7 "market_id": "market-uuid",8 "is_restaurant_user": false,9 "restaurant_location_id": null10 }11}Get List Users
/usersLists users with pagination, search, sorting, advanced column filters, and tenant KPI stats. Existing top-level filters continue to work.
When both top-level
role_idandcolumn_filters.role_idare provided, both filters must match.
Frontend integration
send the selected role UUID either as top-level role_id or as column_filters.role_id. Continue using column_filters.role only for role names. No navigation behavior changes. Invalid filter types return HTTP 400.
Response
includes stats (totalUsers, activeUsers, pendingUsers, recentlyJoined) plus the existing statusCounts block.
Account managers
each returned user includes is_account_manager (tenant designation, independent of role permissions) and account_manager: null or { "id": "user UUID", "name": "manager full name", "is_account_manager": true }. Existing assignments remain visible after their manager is unmarked, with account_manager.is_account_manager: false. Names come from one batched lookup of current user records. The stored assignment remains business_details.account_manager_id. User detail includes these fields in both basic and full responses.
Optional is_account_manager=true|false and column_filters={"is_account_manager":true} filter tenant memberships before pagination. The designation filter also applies to total, statusCounts and stats. Both supplied filters apply together. This informational designation does not change user roles.
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Example: |
pageOptional | string | Page number for pagination (Optional, default: 1) Example: |
limitOptional | string | Number of records per page (Optional, default: 10) Example: |
statusOptional | string | Filter users by status (Optional, values: active, inactive, pending, blocked) Example: |
searchOptional | string | Search term to filter users by email, first name, or last name (Optional) |
tenant_idOptional | string | Filter users by tenant ID (Optional) |
role_idOptional | string | Filter users by role ID (Optional) exact role-ID filter |
startDateOptional | string | Filter users created after this date (ISO format, optional) |
endDateOptional | string | Filter users created before this date (ISO format, optional) |
roleOptional | string | Filter users by exact role name. Supports comma-separated values for multiple roles, Example: |
is_businessOptional | string | Filter by business type: 'true' for business users, 'false' for individual users (Optional) |
show_allOptional | string | Show all users regardless of filters (Optional, default: true) Example: |
include_customerOptional | string | Include users without any assigned roles (customers) in the results (Optional, default: true). Set to 'false' to exclude users without roles. Example: |
exclude_customers | string | Alias for excluding customer rows. Set to 'true' to force |
is_market_userOptional | string | Filter users by market-context flag ( |
market_idOptional | string | Filter users by market ID (Optional) |
is_restaurant_userOptional | string | Filter users by restaurant-context flag ( |
restaurant_location_idOptional | string | Filter users by restaurant location ID (Optional) |
sort_by | string | Sort by one of: |
sort_order | string | Sort order: asc or desc. Defaults to desc when |
column_filters | string | JSON string with column filters. Supported keys: Example: |
is_account_managerOptional | string | Optional true/false designation filter. Also supported in |
column_filters.role_id | string | exact role-ID filter, for example |
column_filters.role | string | exact role-name filter with comma-separated values such as |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/users?language=en&page=1&limit=10&status=active&search=&tenant_id=&role_id=&role=driver,manager&is_business=&startDate=&endDate=&show_all=true&include_customer=true&exclude_customers=&is_market_user=&market_id=&is_restaurant_user=&restaurant_location_id=&sort_by=&sort_order=&column_filters=&is_account_manager=" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
1{2 "success": true,3 "message": "Users retrieved successfully",4 "users": [5 {6 "id": "user-uuid",7 "email": "[email protected]",8 "first_name": "John",9 "last_name": "Doe",10 "full_name": "John Doe",11 "phone": "+1234567890",12 "avatar_url": "https://example.com/avatar.jpg",13 "status": "active",14 "is_market_user": true,15 "market_id": "market-uuid",16 "is_restaurant_user": false,17 "restaurant_location_id": null,18 "user_type": "business",19 "roles": {20 "id": "role-uuid",21 "name": "admin",22 "scope": "tenant"23 },24 "tenant": {25 "id": "tenant-uuid",26 "name": "Example Tenant"27 },28 "created_at": "2023-01-01T00:00:00Z",29 "last_login_at": "2023-01-15T10:30:00Z",30 "is_account_manager": false,31 "account_manager": null32 }33 ],34 "stats": {35 "totalUsers": 42,36 "activeUsers": 30,37 "pendingUsers": 8,38 "recentlyJoined": [39 {40 "id": "user-uuid",41 "full_name": "John Doe",42 "avatar_url": "https://example.com/avatar.jpg",43 "created_at": "2025-09-20T10:42:09.258391+00:00"44 }45 ]46 },47 "page": 1,48 "limit": 10,49 "total": 42,50 "statusCounts": {51 "active": 30,52 "inactive": 8,53 "pending": 4,54 "total": 4255 }56}Get User by ID
/users/:idRetrieves a specific user by ID after target-scope authorization.
Authentication and access
Self-detail remains available.
manage:
user_access:system may read any target.manage:
user_access:tenant may read only a target with an active membership in the caller's exact tenant and no system-scoped role.Missing caller permission returns 403
rbac_forbiddenbefore target lookup.Missing, inactive, foreign-tenant, or system-scoped targets return scope-safe 404
user_not_foundwithout contact or role data.
Both success modes include user.can_reset_password. It is true only when the current JWT caller may call PUT /users/:id/reset-password for that returned user. Full-mode stats and enrichment run only after authorization succeeds.
Frontend integration
open the detail page only on 200. Treat 403 as denied caller access and 404 as unavailable/out-of-scope; do not render cached target data. Show Reset Password only when user.can_reset_password is true.
Account managers
each returned user includes is_account_manager (tenant designation, independent of role permissions) and account_manager: null or { "id": "user UUID", "name": "manager full name", "is_account_manager": true }. Existing assignments remain visible after their manager is unmarked, with account_manager.is_account_manager: false. Names come from one batched lookup of current user records. The stored assignment remains business_details.account_manager_id. User detail includes these fields in both basic and full responses.
Path parameters
| Parameter | Type | Description |
|---|---|---|
idRequired | string | UUID of the user to retrieve (Required) Example: |
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Example: |
response_type | string | Response detail level: 'full' (default) returns all data including roles, permissions, teams, stats; 'basic' returns only essential profile info (id, name, email, phone, status, avatar, Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/users/${ID}?language=en&response_type=full" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
1{2 "success": true,3 "message": "User retrieved successfully",4 "user": {5 "id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",6 "avatar_url": "https://example.com/avatars/a1b2c3d4.png",7 "email": "[email protected]",8 "first_name": "John",9 "last_name": "Doe",10 "full_name": "John Doe",11 "phone": "+15551234567",12 "status": "active",13 "user_type": "member",14 "role": {15 "id": "role-uuid-789",16 "name": "tenant_member",17 "scope": "tenant"18 },19 "tenant_info": {20 "id": "tenant-uuid-abc",21 "name": "Example Tenant Inc.",22 "slug": "example-tenant-inc",23 "status": "active",24 "metadata": {25 "industry": "tech"26 },27 "contact_info": {28 "address": "123 Main St"29 }30 },31 "panel_type": "customer",32 "permissions": [33 "view_deliveries",34 "create_deliveries"35 ],36 "teams": [37 {38 "id": "team-uuid",39 "name": "Team A"40 }41 ],42 "all_teams": false,43 "created_at": "2023-01-15T10:30:00Z",44 "updated_at": "2023-10-20T14:45:00Z",45 "last_login_at": "2023-11-01T09:00:00Z",46 "requires_driver_setup": false,47 "can_reset_password": true,48 "is_account_manager": false,49 "account_manager": null50 },51 "stats": {52 "wallet_balance": 150.5,53 "total_orders": 42,54 "pending_payouts": 2,55 "active_api_keys": 156 }57}Get User Accounting
/users/:userId/accountingReturns the accounting summary used by the user-detail Accounting tab. This endpoint is driver-linked: the user must have a driver record in the tenant.
Includes
Balance summary for selected period (creditor/debtor)
Cash collected (COD + order fee)
Earnings (cash vs online/credit)
Order costs (cash/paid-by-receiver/card/wallet)
Company earnings
Paid withdrawals
From support buckets
Self OR tenant permissions (finance/operations/tenant view).
Path parameters
| Parameter | Type | Description |
|---|---|---|
userIdRequired | string | User UUID (required) Example: |
Query parameters
| Parameter | Type | Description |
|---|---|---|
tenant_idOptional | string | Tenant ID (optional, defaults to auth context) |
from_dateOptional | string | From date (ISO8601, optional) |
to_dateOptional | string | To date (ISO8601, optional) |
languageOptional | string | Language code (optional) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/users/${USERID}/accounting?tenant_id=&from_date=&to_date=&language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
1{2 "success": true,3 "message": "user_accounting_retrieved_successfully",4 "data": {5 "period": {6 "from_date": null,7 "to_date": null8 },9 "currency": "IQD",10 "balance_summary": {11 "amount": 0,12 "type": "creditor",13 "description": "Account is settled."14 },15 "cash_collected": {16 "total": 0,17 "breakdown": {18 "cod": 0,19 "order_fee": 020 }21 },22 "earnings": {23 "total": 0,24 "breakdown": {25 "cash": 0,26 "online_credit": 027 }28 },29 "order_costs": {30 "total": 0,31 "breakdown": {32 "cash": 0,33 "paid_by_receiver": 0,34 "card": 0,35 "wallet": 036 }37 },38 "company_earnings": 0,39 "paid_withdrawals": 0,40 "from_support": {41 "cod_type_creditor": 0,42 "cod_type_debtor": 0,43 "support_type_creditor": 0,44 "support_type_debtor": 045 }46 }47}Invite User
/usersauthenticated JWT or API key. API keys inherit their owner’s permissions; the key’s system/tenant scope remains a ceiling. The api_keys.permissions=[] field is not reinterpreted. The deprecated is_system_admin flag and role names do not authorize these operations.
Errors use {success:false, code, message}; message is localized (en/ar/fa/ku). 403: rbac_forbidden or rbac_invalid_scope. 409: rbac_preservation_conflict, role_already_exists, or the existing last-administrator protection where applicable. Unexpected database errors return 500 with a safe localized message.
Requires manage
user_access:system, or manage:user_access:tenant for the caller’s current tenant. Broad manage:system, manage:tenant and manage:users:tenant permissions do not substitute. Tenant callers may grant only tenant roles whose complete permission set is a subset of their effective permissions. They cannot change system users or operate across tenants. Role scope must match role_type. System assignments have tenant_id=null. Tenant assignments require tenant_id.
Invite a user by email. Required: email, first_name, last_name. Optional: avatar_url, role_id, role_type, tenant_id, user_type, business_details. For add/invite, omitted role_id, null, and "" all mean no role assignment. Non-empty values must be valid role UUIDs and still pass role scope and permission checks. Invalid role values return HTTP 400 with a localized message naming role_id and field-level details; frontend forms can display that message directly. Caller authorization and selected-role validation occur before the invitation email is sent.
role_type defaults to system for manage:user_access:system callers, otherwise tenant. Tenant callers default tenant_id to their own tenant. System callers assigning a tenant role must send role_type=tenant and a valid tenant_id. Authorization and selected-role validation precede Auth work. Existing accounts return 409. Provisioning failures return 500 rbac_provisioning_incomplete; records are retained and disabled, never deleted. Success remains 201 {success:true,message,data}, with data containing the Auth user.
Frontend integration
gate controls using the documented permission names and scope. Display localized message for failures; branch on code/status, never message. Handle 403 as denied access and 409 as a conflict requiring correction. For add/invite only, handle 500 rbac_provisioning_incomplete as an incomplete account kept disabled for administrator recovery. No navigation action is added.
Invitation completion
a pending invited account becomes active when its password changes after its email was confirmed. Supabase assigns a temporary password during invite acceptance, so activation must not depend on the previous password being empty. Accepting the link alone does not activate the account. Inactive accounts remain inactive; ordinary sign-ins and manually created pending accounts retain their status. A confirmed pending invitee can finish activation by setting or resetting their password. Frontend: preserve the Auth session on /update-password so pending invitees can finish password setup.
Contact phone validation
business_details.contact_person.phone remains optional (omitted, null or blank is accepted). A supplied value must contain at least two main-number digits and use phone formatting. Local and international formats, Unicode decimal digits, spaces, parentheses, dots, slashes, hyphens and one leading + are accepted. Optional extensions use ext, ext., extension, x, # or ;ext=. Extensions do not count toward the main-number length. No country-specific length or country code is enforced, and the submitted value is preserved. This checks syntax, not whether the number exists or is reachable. Invalid values return HTTP 400 with a plain localized contact-phone message, without an internal field-path prefix. The field identifier remains in structured details for clients; display the top-level message in the portal toast. Validation runs before invitation email delivery or account writes.
Invitation delivery errors
Auth failures keep HTTP 400 and return {success:false,code,message}. Codes are invitation_email_rejected, invitation_email_not_authorized, invitation_rate_limited, user_already_exists, or invitation_failed. Recipient errors also include details:[{field:"email",message}]. Messages explain the failed step and the next action; raw provider errors are never returned. Unknown Auth failures cannot reliably identify the SMTP cause, so the fallback asks the user to check the address and supported test address, then asks an administrator to check delivery settings. Existing application-account lookup conflicts remain HTTP 409. For staging invitation tests use a controlled real mailbox or the email provider's supported test recipient, such as [email protected]; example.com recipients are rejected by the current provider. Frontend forms can continue showing the localized message; no response navigation changes.
Account-manager assignment
business_details.account_manager_id accepts a designated same-tenant user UUID, null, or an empty string to clear. A new/changed manager must have is_account_manager=true; creation validates before creating the Auth account. Update preserves an unchanged assignment even after the manager is unmarked. Invalid manager: HTTP 400, code: invalid_account_manager_id, localized message. Assignment changes require manage:user_access:tenant for the target tenant or manage:user_access:system.
Optional is_account_manager must be a boolean (null is rejected). It is a tenant designation, grants no permissions and defaults to false for new memberships. Unmarking keeps all existing assignments but excludes the user from future picker results. Update omission preserves the flag; ordinary self-profile access cannot change it. A supplied tenant ID must match the target membership. Forbidden permissions/scope: HTTP 403. Frontend: refresh the existing user/customer data and picker after a successful change.
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
email | string | — |
first_name | string | — |
last_name | string | — |
avatar_url | string | — |
role_id | string | — |
is_account_manager | boolean | — |
business_details | object | — |
business_details.account_manager_id | null | — |
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/users?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "email": "[email protected]",6 "first_name": "John",7 "last_name": "Doe",8 "avatar_url": "https://example.com/avatar.png",9 "role_id": "role-uuid-optional",10 "is_account_manager": false,11 "business_details": {12 "account_manager_id": null13 }14}'Response examples
1{2 "success": true,3 "message": "invitation_sent_successfully",4 "data": {5 "user": {6 "id": "new-user-uuid",7 "email": "[email protected]"8 }9 }10}Add User (auto-confirm)
/users/addauthenticated JWT or API key. API keys inherit their owner’s permissions; the key’s system/tenant scope remains a ceiling. The api_keys.permissions=[] field is not reinterpreted. The deprecated is_system_admin flag and role names do not authorize these operations.
Errors use {success:false, code, message}; message is localized (en/ar/fa/ku). 403: rbac_forbidden or rbac_invalid_scope. 409: rbac_preservation_conflict, role_already_exists, or the existing last-administrator protection where applicable. Unexpected database errors return 500 with a safe localized message.
Requires manage
user_access:system, or manage:user_access:tenant for the caller’s current tenant. Broad manage:system, manage:tenant and manage:users:tenant permissions do not substitute. Tenant callers may grant only tenant roles whose complete permission set is a subset of their effective permissions. They cannot change system users or operate across tenants. Role scope must match role_type. System assignments have tenant_id=null. Tenant assignments require tenant_id.
Create an auto-confirmed user. Required: password and at least one of email/phone. Optional: first_name, last_name, avatar_url, role_id, role_type, tenant_id, is_staff, user_type, business_details. For add/invite, omitted role_id, null, and "" all mean no role assignment. Non-empty values must be valid role UUIDs and still pass role scope and permission checks. Invalid role values return HTTP 400 with a localized message naming role_id and field-level details; frontend forms can display that message directly. Only manage:user_access:system permits is_staff=true. Auth creation starts banned; successful membership/role provisioning is followed by unbanning.
role_type defaults to system for manage:user_access:system callers, otherwise tenant. Tenant callers default tenant_id to their own tenant. System callers assigning a tenant role must send role_type=tenant and a valid tenant_id. Authorization and selected-role validation precede Auth work. Existing accounts return 409. Provisioning failures return 500 rbac_provisioning_incomplete; records are retained and disabled, never deleted. Success remains 201 {success:true,message,data}, with data containing the Auth user.
Frontend integration
gate controls using the documented permission names and scope. Display localized message for failures; branch on code/status, never message. Handle 403 as denied access and 409 as a conflict requiring correction. For add/invite only, handle 500 rbac_provisioning_incomplete as an incomplete account kept disabled for administrator recovery. No navigation action is added.
Account status
optional status accepts active, inactive, or pending; defaults to pending when omitted (or null). After successful role/membership provisioning, the selected application status is persisted before authentication is unbanned. Email/phone remain auto-confirmed. Pending/inactive accounts cannot access application APIs until an administrator activates them. Frontend: Add User status selector defaults to Active and explicitly sends the selection. Existing API clients that omit status now create pending accounts.
Contact phone validation
business_details.contact_person.phone remains optional (omitted, null or blank is accepted). A supplied value must contain at least two main-number digits and use phone formatting. Local and international formats, Unicode decimal digits, spaces, parentheses, dots, slashes, hyphens and one leading + are accepted. Optional extensions use ext, ext., extension, x, # or ;ext=. Extensions do not count toward the main-number length. No country-specific length or country code is enforced, and the submitted value is preserved. This checks syntax, not whether the number exists or is reachable. Invalid values return HTTP 400 with a plain localized contact-phone message, without an internal field-path prefix. The field identifier remains in structured details for clients; display the top-level message in the portal toast. Validation runs before invitation email delivery or account writes.
Account-manager assignment
business_details.account_manager_id accepts a designated same-tenant user UUID, null, or an empty string to clear. A new/changed manager must have is_account_manager=true; creation validates before creating the Auth account. Update preserves an unchanged assignment even after the manager is unmarked. Invalid manager: HTTP 400, code: invalid_account_manager_id, localized message. Assignment changes require manage:user_access:tenant for the target tenant or manage:user_access:system.
Optional is_account_manager must be a boolean (null is rejected). It is a tenant designation, grants no permissions and defaults to false for new memberships. Unmarking keeps all existing assignments but excludes the user from future picker results. Update omission preserves the flag; ordinary self-profile access cannot change it. A supplied tenant ID must match the target membership. Forbidden permissions/scope: HTTP 403. Frontend: refresh the existing user/customer data and picker after a successful change.
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
email | string | — |
phone | string | — |
password | string | — |
first_name | string | — |
last_name | string | — |
avatar_url | string | — |
is_staff | boolean | — |
role_id | string | — |
status | string | — |
is_account_manager | boolean | — |
business_details | object | — |
business_details.account_manager_id | null | — |
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/users/add?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "email": "[email protected]",6 "phone": "+1234567890",7 "password": "SecurePassword123!",8 "first_name": "New",9 "last_name": "User",10 "avatar_url": "https://example.com/avatar.png",11 "is_staff": false,12 "role_id": "role-uuid-optional",13 "status": "active",14 "is_account_manager": false,15 "business_details": {16 "account_manager_id": null17 }18}'Response examples
1{2 "success": true,3 "message": "user_created_successfully",4 "data": {5 "user": {6 "id": "new-user-uuid",7 "email": "[email protected]",8 "phone": "+1234567890"9 }10 }11}Add Customer (auto-confirm phone)
/users/add-customerCreates a customer user with phone and name. Phone is auto-confirmed; email is optional. Password is generated automatically.
Contact phone validation
business_details.contact_person.phone remains optional (omitted, null or blank is accepted). A supplied value must contain at least two main-number digits and use phone formatting. Local and international formats, Unicode decimal digits, spaces, parentheses, dots, slashes, hyphens and one leading + are accepted. Optional extensions use ext, ext., extension, x, # or ;ext=. Extensions do not count toward the main-number length. No country-specific length or country code is enforced, and the submitted value is preserved. This checks syntax, not whether the number exists or is reachable. Invalid values return HTTP 400 with a plain localized contact-phone message, without an internal field-path prefix. The field identifier remains in structured details for clients; display the top-level message in the portal toast. Validation runs before invitation email delivery or account writes.
Account-manager assignment
business_details.account_manager_id accepts a designated same-tenant user UUID, null, or an empty string to clear. A new/changed manager must have is_account_manager=true; creation validates before creating the Auth account. Update preserves an unchanged assignment even after the manager is unmarked. Invalid manager: HTTP 400, code: invalid_account_manager_id, localized message. Assignment changes require manage:user_access:tenant for the target tenant or manage:user_access:system.
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
name | string | — |
phone | string | — |
email | string | — |
business_details | object | — |
business_details.account_manager_id | null | — |
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/users/add-customer?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "name": "Jane Customer",6 "phone": "+1234567890",7 "email": "[email protected]",8 "business_details": {9 "account_manager_id": null10 }11}'Response examples
1{2 "success": true,3 "message": "user_created_successfully",4 "data": {5 "user": {6 "id": "customer-uuid",7 "phone": "+1234567890",8 "email": "[email protected]"9 }10 }11}Resend User Invitation
/users/resend-invitationResends an invitation email to a user who was previously invited but may not have received or acted on the original invitation.
Must have appropriate permissions to invite users
Endpoint
POST /users/resend-invitation
Required Fields
email: Valid email address of the user to resend invitation to
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
email | string | — |
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/users/resend-invitation?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "email": "[email protected]"6}'Response examples
1{2 "success": true,3 "message": "Invitation resent successfully",4 "data": {5 "user": {6 "id": "user-uuid",7 "email": "[email protected]",8 "invited_at": "2023-01-01T00:00:00Z"9 }10 }11}List Customers
/users/customersLists customers (users without assigned roles) with pagination, search, sorting, column filters, and KPI stats. Legacy top-level is_business still works.
Requires tenant context
Response
Account managers
each returned user includes is_account_manager (tenant designation, independent of role permissions) and account_manager: null or { "id": "user UUID", "name": "manager full name", "is_account_manager": true }. Existing assignments remain visible after their manager is unmarked, with account_manager.is_account_manager: false. Names come from one batched lookup of current user records. The stored assignment remains business_details.account_manager_id. User detail includes these fields in both basic and full responses.
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Example: |
pageOptional | string | Page number for pagination (Optional, default: 1) Example: |
limitOptional | string | Number of records per page (Optional, default: 10) Example: |
tenant_idOptional | string | Tenant ID to filter customers (Optional, uses context if not provided) Example: |
is_businessOptional | string | Filter by customer type: true for business, false for individual (Optional) |
is_market_userOptional | string | Filter customers by market-context flag ( |
market_idOptional | string | Filter customers by market ID (Optional) |
is_restaurant_userOptional | string | Filter customers by restaurant-context flag ( |
restaurant_location_idOptional | string | Filter customers by restaurant location ID (Optional) |
search | string | Toolbar search across |
sort_by | string | Sort by one of: name, email, |
sort_order | string | Sort order: asc or desc. Defaults to desc when |
column_filters | string | JSON string with keys: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
More examples
1{2 "success": true,3 "message": "Users retrieved successfully",4 "users": [5 {6 "id": "user-uuid",7 "email": "[email protected]",8 "first_name": "Jane",9 "last_name": "Doe",10 "full_name": "Jane Doe",11 "user_type": "individual",12 "is_market_user": false,13 "market_id": null,14 "is_restaurant_user": false,15 "restaurant_location_id": null,16 "roles": {17 "id": null,18 "name": "customer",19 "scope": "tenant"20 },21 "tenant": {22 "id": "tenant-uuid",23 "name": "Example Tenant"24 },25 "last_login_at": "2025-09-22T08:30:00Z"26 }27 ],28 "stats": {29 "totalUsers": 25,30 "activeUsers": 20,31 "pendingUsers": 3,32 "recentlyJoined": [33 {34 "id": "user-uuid",35 "first_name": "Jane",36 "last_name": "Doe",37 "email": "[email protected]",38 "avatar_url": null,39 "created_at": "2025-09-20T10:42:09.258391+00:00"40 }41 ]42 },43 "page": 1,44 "limit": 10,45 "total": 2546}1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/users/customers?language=en&page=1&limit=10&tenant_id=${TENANT_ID}&is_business=&is_market_user=&market_id=&is_restaurant_user=&restaurant_location_id=&search=&sort_by=&sort_order=&column_filters=" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
1{2 "success": true,3 "message": "Users retrieved successfully",4 "users": [5 {6 "id": "user-uuid",7 "email": "[email protected]",8 "first_name": "Jane",9 "last_name": "Doe",10 "full_name": "Jane Doe",11 "user_type": "individual",12 "is_market_user": false,13 "market_id": null,14 "is_restaurant_user": false,15 "restaurant_location_id": null,16 "roles": {17 "id": null,18 "name": "customer",19 "scope": "tenant"20 },21 "tenant": {22 "id": "tenant-uuid",23 "name": "Example Tenant"24 },25 "is_account_manager": false,26 "account_manager": null27 }28 ],29 "stats": {30 "totalUsers": 25,31 "activeUsers": 20,32 "pendingUsers": 3,33 "recentlyJoined": [34 {35 "id": "user-uuid",36 "first_name": "Jane",37 "last_name": "Doe",38 "email": "[email protected]",39 "avatar_url": null,40 "created_at": "2025-09-20T10:42:09.258391+00:00"41 }42 ]43 },44 "page": 1,45 "limit": 10,46 "total": 2547}Update User
/users/:idUpdates a user's information. Users can update their own information, while system administrators can update any user.
Users can update their own profile (including
status)System administrators / tenant administrators can update any user profile (email change is self-only)
Updatable Fields
email: Change login email (self-only). Triggers a confirmation email and redirects to sign-in after confirmation.first_name: User's first namelast_name: User's last namestatus: User account status (self or admin)phone: User's phone numberavatar_url: URL to user's profile imageprofile_data: Object containing additional profile informationcustomer_delivery_preferences: Object containing delivery preferences
Notes
Only provided fields will be updated. Fields not included in the request will remain unchanged.
Email change sends a confirmation email via Supabase Auth and is allowed only when updating your own user.
When email is updated, the API returns immediately with
requires_email_confirmation: trueandnew_email. The frontend should display the message instructing the user to check their new email inbox to confirm.
Response
Status editing
pending users may now be edited, activated, or deactivated through this endpoint. A supplied status must be active or inactive; omit it to preserve the existing status, including pending. Existing authorization and administrator deactivation protections remain in effect. Status affects the global application account, not an individual tenant membership, and does not confirm email or set a password. Frontend: show the current status, including Pending as a display-only option, submit status only when changed, keep the modal open on failure, and refresh the user on success. An unsupported status returns HTTP 400.
Contact phone validation
business_details.contact_person.phone remains optional (omitted, null or blank is accepted). A supplied value must contain at least two main-number digits and use phone formatting. Local and international formats, Unicode decimal digits, spaces, parentheses, dots, slashes, hyphens and one leading + are accepted. Optional extensions use ext, ext., extension, x, # or ;ext=. Extensions do not count toward the main-number length. No country-specific length or country code is enforced, and the submitted value is preserved. This checks syntax, not whether the number exists or is reachable. Invalid values return HTTP 400 with a plain localized contact-phone message, without an internal field-path prefix. The field identifier remains in structured details for clients; display the top-level message in the portal toast. Validation runs before invitation email delivery or account writes.
Account-manager assignment
business_details.account_manager_id accepts a designated same-tenant user UUID, null, or an empty string to clear. A new/changed manager must have is_account_manager=true; creation validates before creating the Auth account. Update preserves an unchanged assignment even after the manager is unmarked. Invalid manager: HTTP 400, code: invalid_account_manager_id, localized message. Assignment changes require manage:user_access:tenant for the target tenant or manage:user_access:system.
Optional is_account_manager must be a boolean (null is rejected). It is a tenant designation, grants no permissions and defaults to false for new memberships. Unmarking keeps all existing assignments but excludes the user from future picker results. Update omission preserves the flag; ordinary self-profile access cannot change it. A supplied tenant ID must match the target membership. Forbidden permissions/scope: HTTP 403. Frontend: refresh the existing user/customer data and picker after a successful change.
Path parameters
| Parameter | Type | Description |
|---|---|---|
idRequired | string | UUID of the user to update (Required) Example: |
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Language code for localized response messages ( Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
email | string | — |
first_name | string | — |
last_name | string | — |
status | string | — |
phone | string | — |
avatar_url | string | — |
profile_data | object | — |
profile_data.bio | string | — |
profile_data.preferences | object | — |
profile_data.preferences.theme | string | — |
profile_data.preferences.notifications | boolean | — |
customer_delivery_preferences | object | — |
Show 5 more body fields
Additional body parameters
| Parameter | Type | Description |
|---|---|---|
customer_delivery_preferences.preferred_time | string | — |
customer_delivery_preferences.special_instructions | string | — |
is_account_manager | boolean | — |
business_details | object | — |
business_details.account_manager_id | null | — |
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/users/${ID}?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "email": "[email protected]",6 "first_name": "Updated",7 "last_name": "Name",8 "status": "inactive",9 "phone": "+9876543210",10 "avatar_url": "https://example.com/avatar.jpg",11 "profile_data": {12 "bio": "Updated user bio",13 "preferences": {14 "theme": "dark",15 "notifications": true16 }17 },18 "customer_delivery_preferences": {19 "preferred_time": "evening",20 "special_instructions": "Leave at the door"21 },22 "is_account_manager": false,23 "business_details": {24 "account_manager_id": null25 }26}'Other request examples (1)
1{2 "email": "[email protected]",3 "first_name": "Updated",4 "last_name": "Name",5 "status": "inactive",6 "phone": "+9876543210",7 "avatar_url": "https://example.com/avatar.jpg",8 "profile_data": {9 "bio": "Updated user bio",10 "preferences": {11 "theme": "dark",12 "notifications": true13 }14 },15 "customer_delivery_preferences": {16 "preferred_time": "evening",17 "special_instructions": "Leave at the door"18 }19}Response examples
1{2 "success": true,3 "message": "User updated successfully",4 "user": {5 "id": "user-uuid",6 "email": "[email protected]",7 "first_name": "Updated",8 "last_name": "Name",9 "status": "inactive",10 "phone": "+9876543210",11 "avatar_url": "https://example.com/avatar.jpg",12 "profile_data": {13 "bio": "Updated user bio",14 "preferences": {15 "theme": "dark",16 "notifications": true17 }18 },19 "customer_delivery_preferences": {20 "preferred_time": "evening",21 "special_instructions": "Leave at the door"22 },23 "created_at": "2023-01-01T00:00:00Z",24 "updated_at": "2023-01-01T00:00:00Z"25 }26}Update My Password
/users/me/passwordUpdates the current authenticated user's password.
Requires valid authenticated user bearer token
API key authentication is not allowed
Required Fields
new_password: New password (min: 8, max: 128 characters)
Optional Fields
current_password: Required when the user already has a password. Omit it only when setting a password for the first time after OTP or magic link sign-in.
Notes
The authenticated user is always the target user
Users with an existing password must provide the correct current password
Users without an existing password can set one by providing only
new_password
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Language code for localized response messages ( Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
current_password | string | — |
new_password | string | — |
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/users/me/password?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "current_password": "CurrentSecurePassword123!",6 "new_password": "NewSecurePassword123!"7}'Response examples
1{2 "success": true,3 "message": "Password updated successfully",4 "data": {5 "user_id": "user-uuid"6 }7}Update My Notification Settings
/users/me/notificationsEnable or disable in-app/push/email/SMS notifications for the current user.
When notifications_enabled is false, new rows in notifications are stored with status skipped_disabled_notification and are not delivered; they do not appear in GET /messaging/notifications (which only returns status: sent).
Bearer token only (API key not supported).
Response (200)
{ success, message, data: { user_id, notifications_enabled } }
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
notifications_enabledRequired | boolean | — |
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/users/me/notifications?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "notifications_enabled": true6}'Response examples
1{2 "success": true,3 "message": "Notification settings updated successfully",4 "data": {5 "user_id": "user-uuid",6 "notifications_enabled": true7 }8}Reset User Password by ID
/users/:id/reset-passwordResets a user's password by ID after permission and target-scope authorization.
JWT only; API keys return 403
rbac_forbidden.manage:
user_access:system may reset any target.manage:
user_access:tenant may reset only a target with an active membership in the caller's exact tenant and no system-scoped role.Role names and the deprecated
is_system_adminflag do not authorize this operation.
The complete target boundary is checked in one indexed query before auth.admin.updateUserById. Missing caller permission returns 403 rbac_forbidden. Missing, inactive, foreign-tenant, or system-scoped targets return scope-safe 404 user_not_found and the password is unchanged.
{"password":"NewSecurePassword123!"}; password length is 8-128 characters. Success remains 200 with data.user_id, email, and phone.
Frontend integration
send the returned user ID only when user.can_reset_password from GET /users/:id is true. Handle 403 as denied caller access and 404 as unavailable/out-of-scope. The backend check remains authoritative.
Path parameters
| Parameter | Type | Description |
|---|---|---|
idRequired | string | UUID of the user whose password to reset (Required) Example: |
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
password | string | — |
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/users/${ID}/reset-password?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "password": "NewSecurePassword123!"6}'Response examples
1{2 "success": true,3 "message": "User updated successfully",4 "data": {5 "user_id": "user-uuid",6 "email": "[email protected]",7 "phone": "+1234567890"8 }9}Delete User
/users/:idDelete a user. This operation may be permanent or may mark the user as deleted depending on implementation.
Path parameters
| Parameter | Type | Description |
|---|---|---|
idRequired | string | UUID of the user to delete (Required) Example: |
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request DELETE "https://staging-api.ondi.io/functions/v1/users/${ID}?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
Completed successfully. No response body.
Assign User Role
/users/:userId/rolesauthenticated JWT or API key. API keys inherit their owner’s permissions; the key’s system/tenant scope remains a ceiling. The api_keys.permissions=[] field is not reinterpreted. The deprecated is_system_admin flag and role names do not authorize these operations.
Errors use {success:false, code, message}; message is localized (en/ar/fa/ku). 403: rbac_forbidden or rbac_invalid_scope. 409: rbac_preservation_conflict, role_already_exists, or the existing last-administrator protection where applicable. Unexpected database errors return 500 with a safe localized message.
Requires manage
user_access:system, or manage:user_access:tenant for the caller’s current tenant. Broad manage:system, manage:tenant and manage:users:tenant permissions do not substitute. Tenant callers may grant only tenant roles whose complete permission set is a subset of their effective permissions. They cannot change system users or operate across tenants. Role scope must match role_type. System assignments have tenant_id=null. Tenant assignments require tenant_id.
Required
role_id. System user-access callers must explicitly send role_type. Tenant callers default role_type=tenant and tenant_id to their current tenant. Optional tenant_id for system callers assigning tenant roles. Malformed JSON, scalar bodies and malformed UUIDs return 400. Self changes return 403; changing the last active system_admin or tenant_admin returns 409. A business user without an existing role cannot be assigned one (400). Authorization uses the permissions already loaded by authentication middleware. Membership and role assignment use separate database writes; failures stop the remaining steps but earlier writes may persist. Existing assignments are updated in place. Audit logging uses the existing request middleware. Success remains 201 for a new assignment, 200 for an existing assignment, {success:true,message}.
Frontend integration
gate controls using the documented permission names and scope. Display localized message for failures; branch on code/status, never message. Handle 403 as denied access and 409 as a conflict requiring correction. For add/invite only, handle 500 rbac_provisioning_incomplete as an incomplete account kept disabled for administrator recovery. No navigation action is added.
Path parameters
| Parameter | Type | Description |
|---|---|---|
userIdRequired | string | The unique identifier of the user to assign the role to |
Query parameters
| Parameter | Type | Description |
|---|---|---|
language | string | Language code for response messages (default: en) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
Content-Type | string | Example: |
Body parameters
| Parameter | Type | Description |
|---|---|---|
role_id | string | — |
role_type | string | — |
tenant_id | string | — |
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/users/${USERID}/roles?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3 --header "Content-Type: application/json" \4 --data-raw '{5 "role_id": "role-uuid",6 "role_type": "tenant",7 "tenant_id": "tenant-uuid"8}'Response examples
1{2 "success": true,3 "message": "Role assigned successfully"4}Get User Role
/users/:userId/rolesRetrieves roles assigned to a user. This endpoint returns all roles associated with the specified user, including role name, scope, role type, and tenant ID.
Path parameters
| Parameter | Type | Description |
|---|---|---|
userIdRequired | string | ID of the user to get roles for |
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/users/${USERID}/roles?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
1{2 "success": true,3 "message": "Role retrieved successfully",4 "roles": [5 {6 "id": "role-123",7 "name": "Admin",8 "scope": "global",9 "role_type": "admin",10 "tenant_id": "tenant-456"11 },12 {13 "id": "role-789",14 "name": "Editor",15 "scope": "tenant",16 "role_type": "user",17 "tenant_id": "tenant-456"18 }19 ]20}Remove User Role
/users/:userId/roles/:roleIdauthenticated JWT or API key. API keys inherit their owner’s permissions; the key’s system/tenant scope remains a ceiling. The api_keys.permissions=[] field is not reinterpreted. The deprecated is_system_admin flag and role names do not authorize these operations.
Errors use {success:false, code, message}; message is localized (en/ar/fa/ku). 403: rbac_forbidden or rbac_invalid_scope. 409: rbac_preservation_conflict, role_already_exists, or the existing last-administrator protection where applicable. Unexpected database errors return 500 with a safe localized message.
Requires manage
user_access:system, or manage:user_access:tenant for the caller’s current tenant. Broad manage:system, manage:tenant and manage:users:tenant permissions do not substitute. Tenant callers may grant only tenant roles whose complete permission set is a subset of their effective permissions. They cannot change system users or operate across tenants. Role scope must match role_type. System assignments have tenant_id=null. Tenant assignments require tenant_id.
Record removal is blocked by the preservation policy. Unauthorized callers receive 403. Authorized callers receive 409 rbac_preservation_conflict. No role, permission assignment, user assignment, or dependent record is deleted. There is no success response while this policy is active.
Frontend integration
gate controls using the documented permission names and scope. Display localized message for failures; branch on code/status, never message. Handle 403 as denied access and 409 as a conflict requiring correction. For add/invite only, handle 500 rbac_provisioning_incomplete as an incomplete account kept disabled for administrator recovery. No navigation action is added.
Path parameters
| Parameter | Type | Description |
|---|---|---|
userIdRequired | string | ID of the user to remove the role from |
roleIdRequired | string | ID of the role to be removed from the user |
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request DELETE "https://staging-api.ondi.io/functions/v1/users/${USERID}/roles/${ROLEID}?language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
1{2 "success": false,3 "code": "rbac_forbidden",4 "message": "You do not have permission to perform this action."5}Get User Addresses
/users/:userId/addressesLists addresses belonging to a specific user with pagination. Users can only access their own addresses, while system administrators can access any user's addresses.
Users can only access their own addresses
System administrators can access any user's addresses
Path parameters
| Parameter | Type | Description |
|---|---|---|
userIdRequired | string | UUID of the user whose addresses to retrieve (Required) Example: |
Query parameters
| Parameter | Type | Description |
|---|---|---|
languageOptional | string | Language code for response localization (Optional, default: en) Language code for localized response messages ( Example: |
pageOptional | string | Page number for pagination (Optional, default: 1) Page number for pagination (default: 1) Example: |
limitOptional | string | Number of records per page (Optional, default: 10) Number of records per page (default: 10) Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/users/${USERID}/addresses?language=en&page=1&limit=10" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
1{2 "success": true,3 "message": "Addresses retrieved successfully",4 "addresses": [5 {6 "id": "address-uuid",7 "user_id": "user-uuid",8 "title": "Home",9 "address_line1": "123 Main St",10 "address_line2": "Apt 4B",11 "city": "New York",12 "state": "NY",13 "postal_code": "10001",14 "country": "US",15 "is_primary": true,16 "is_active": true,17 "latitude": 40.7128,18 "longitude": -74.006,19 "created_at": "2023-01-01T00:00:00Z",20 "updated_at": "2023-01-01T00:00:00Z"21 }22 ],23 "total": 5,24 "page": 1,25 "limit": 1026}List Account Managers
/users/account-managersSlim account-manager picker. Requires manage:user_access:tenant or manage:user_access:system and tenant access. Returns only users whose tenant_users.is_account_manager=true in the requested tenant, without a role or active-status condition. One focused joined database query filters and paginates; no statistics or detail requests. Stable order: user ID. Debounce search and load the next page while pagination.has_more is true. Display an existing unmarked assignment using account_manager from user/customer responses; never add it as a new picker option.
Query parameters
| Parameter | Type | Description |
|---|---|---|
tenant_id | string | Required when no tenant context; tenant callers may only request their own tenant. Example: |
searchOptional | string | Optional literal full-name substring; up to 100 characters. |
page | string | Positive integer. Example: |
limit | string | 1–100; defaults to 25. Example: |
language | string | Example: |
Headers
| Parameter | Type | Description |
|---|---|---|
Authorization | string | Example: |
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/users/account-managers?tenant_id=${TENANT_ID}&search=&page=1&limit=25&language=en" \2 --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"Response examples
1{2 "success": true,3 "users": [4 {5 "id": "00000000-0000-4000-8000-000000000001",6 "full_name": "Alex Manager"7 }8 ],9 "pagination": {10 "page": 1,11 "limit": 25,12 "has_more": false13 }14}