Skip to main content

Tenants

23 API calls in this section.

List Tenants​

Platform & Access / Tenants
GET/tenants

Retrieves a paginated list of tenants in the system. This endpoint supports filtering by status and searching by tenant slug.

  • Only system administrators can access this endpoint

Query parameters

ParameterTypeDescription
languageOptionalstring

Language code for response localization (Optional, default: en) Language code for localized response messages (e.g., 'en')

Example: en

pageOptionalstring

Page number for pagination (Optional, default: 1) Page number for pagination (default: 1)

Example: 1

limitOptionalstring

Number of results per page (Optional, default: 10, max: 100) Number of results per page (default: 10, max: 100)

Example: 10

statusOptionalstring

Filter tenants by status (Optional, e.g., 'active', 'pending', 'suspended') Filter tenants by status (e.g., 'active', 'pending', 'suspended')

searchOptionalstring

Search term to filter tenants by slug (Optional)

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/tenants?language=en&page=1&limit=10&status=&search=" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200Success Response
Response body
json
1{2  "success": true,3  "message": "Tenants retrieved successfully",4  "tenants": [5    {6      "id": "550e8400-e29b-41d4-a716-446655440000",7      "name": {8        "en": "Tenant Name"9      },10      "slug": "tenant-slug",11      "status": "active",12      "contact_info": {},13      "metadata": {},14      "created_at": "2023-01-01T00:00:00Z",15      "created_by": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",16      "updated_at": "2023-01-01T00:00:00Z",17      "updated_by": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",18      "tenant_users": {19        "count": 520      }21    }22  ],23  "total": 25,24  "page": 1,25  "limit": 1026}

Create Tenant​

Platform & Access / Tenants
POST/tenants

Creates a new tenant in the system. This endpoint is restricted to system administrators only.

  • Only system administrators can access this endpoint

Important

  • Tenant subscription gate fields are normalized immediately after creation via recompute_tenant_status (respects the global tenant_subscription.enabled setting).

Required Fields

  • name: Object containing multilingual tenant names (at least one language required)

  • slug: URL-friendly identifier (must be unique, lowercase alphanumeric with hyphens only)

Optional Fields

  • contact_info: Object containing contact information (email, phone, address, etc.)

  • metadata: Object containing additional tenant metadata

Validations

  • Slug must match pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$

  • Slug must be unique across all tenants

  • Name must be provided in at least one language

  • Each language name must be a non-empty string

Query parameters

ParameterTypeDescription
languageOptionalstring

Language of the response Language code for localized response messages (e.g., 'en')

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
nameobject—
name.enstring—
name.frstring—
slugstring—
contact_infoobject—
contact_info.emailstring—
contact_info.phonestring—
contact_info.addressstring—
metadataobject—
metadata.industrystring—
metadata.sizestring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/tenants?language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "name": {6    "en": "Tenant Name",7    "fr": "Nom du Locataire"8  },9  "slug": "tenant-slug",10  "contact_info": {11    "email": "contact@example.com",12    "phone": "+1234567890",13    "address": "123 Main St"14  },15  "metadata": {16    "industry": "technology",17    "size": "enterprise"18  }19}'

Response examples

201Created
Response body
json
1{2  "success": true,3  "message": "Tenant created successfully",4  "tenant": {5    "id": "550e8400-e29b-41d4-a716-446655440000",6    "name": {7      "en": "Tenant Name",8      "fr": "Nom du Locataire"9    },10    "slug": "tenant-slug",11    "contact_info": {12      "email": "contact@example.com",13      "phone": "+1234567890",14      "address": "123 Main St"15    },16    "status": "pending",17    "metadata": {18      "industry": "technology",19      "size": "enterprise"20    },21    "subscription_status": "NONE",22    "effective_active": true,23    "effective_until": null,24    "created_at": "2023-01-01T00:00:00Z",25    "created_by": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",26    "created_by_user": {27      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",28      "email": "admin@example.com"29    }30  }31}

Create Tenant Wizard​

Platform & Access / Tenants
POST/tenants/wizard

Creates a fully configured tenant in a single API call. If any step fails, the endpoint performs compensating rollback (best-effort) by deleting created resources (tenant + created admin user) and canceling Stripe objects when possible.

  • Only system administrators can access this endpoint

Subscription

  • When system setting tenant_subscription.enabled=true: subscription is required (type: MONTHLY|ANNUAL|UNLIMITED, amount)

  • When tenant_subscription.enabled=false: subscription is optional and ignored; no Stripe/subscription records created

Notes

  • Admin user is created immediately (password required)

  • Email/phone are derived from tenant.contact_info

  • Tenant admin role is resolved automatically

  • Admin avatar uses brand_settings.assets.logo.light when provided, else tenant.logo_url

Error

Admin-user creation authorization

when admin_user.create=true, also requires manage:user_access:system with a system-capable JWT/API-key context. This check happens before tenant/Auth/role creation. The existing manage:system prerequisite remains. Requests without admin creation retain their existing authorization. Missing the new permission returns 403 {success:false,code:"rbac_forbidden",message:<localized>}. Frontend integration: require the new permission for the create-admin option and handle 403.

Query parameters

ParameterTypeDescription
languageOptionalstring

Language code for response localization (Optional, default: en)

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
tenantobject—
tenant.nameobject—
tenant.name.enstring—
tenant.name.arstring—
tenant.name.kustring—
tenant.slugstring—
tenant.contact_infoobject—
tenant.contact_info.emailstring—
tenant.contact_info.phonestring—
tenant.logo_urlstring—
admin_userobject—
admin_user.createboolean—
Show 89 more body fields

Additional body parameters

ParameterTypeDescription
admin_user.passwordstring—
modulesobject—
modules.enabled_module_idsarray<string>—
subscriptionobject—
subscription.typestring—
subscription.amountnumber—
subscription.note_publicstring—
subscription.note_internalstring—
brand_settingsobject—
brand_settings.brand_nameobject—
brand_settings.brand_name.enstring—
brand_settings.brand_name.arstring—
brand_settings.brand_name.kustring—
brand_settings.assetsobject—
brand_settings.assets.logoobject—
brand_settings.assets.logo.lightstring—
brand_settings.assets.logo.darkstring—
brand_settings.assets.faviconstring—
brand_settings.colorsobject—
brand_settings.colors.textstring—
brand_settings.colors.accentstring—
brand_settings.colors.primarystring—
brand_settings.colors.secondarystring—
brand_settings.colors.backgroundstring—
brand_settings.configurationsobject—
brand_settings.configurations.fontstring—
brand_settings.configurations.themestring—
brand_settings.configurations.animationsboolean—
brand_settings.configurations.rtl_supportboolean—
general_configobject—
general_config.unit_sizeobject—
general_config.unit_size.unit_lengthnumber—
general_config.unit_size.unit_weightnumber—
general_config.unit_size.weight_unitstring—
general_config.unit_size.volumetric_divisornumber—
general_config.unit_size.dimension_unitstring—
general_config.unit_size.use_volumetric_weightboolean—
general_config.locationobject—
general_config.location.latitudenumber—
general_config.location.longitudenumber—
general_config.currencyobject—
general_config.currency.currency_codestring—
general_config.currency.currency_namestring—
general_config.currency.currency_symbolstring—
general_config.currency.currency_modestring—
general_config.currency.exchange_rate_to_systemnumber—
general_config.domestic_countryobject—
general_config.domestic_country.codestring—
general_config.domestic_country.namestring—
general_config.domestic_country.dial_codestring—
general_config.driverobject—
general_config.driver.modestring—
general_config.driver.percentageobject—
general_config.driver.percentage.percentage_of_delivery_feenumber—
general_config.driver.per_deliveryobject—
general_config.driver.per_delivery.base_amountnumber—
general_config.driver.driver_can_self_assignboolean—
general_config.driver.require_accept_or_reject_for_assignmentsboolean—
general_config.integrationobject—
general_config.integration.sms_channelobject—
general_config.integration.sms_channel.selected_channelstring—
general_config.warehouseobject—
general_config.warehouse.handoff_requiredboolean—
general_config.express_shippingobject—
general_config.express_shipping.customer_prefix_keystring—
general_config.user_settingsobject—
general_config.user_settings.offline_threshold_secondsnumber—
general_config.user_settings.heartbeat_interval_secondsnumber—
general_config.mobileobject—
general_config.mobile.show_tenant_list_pageboolean—
general_config.storefrontobject—
general_config.storefront.review_auto_approvalobject—
general_config.storefront.review_auto_approval.enabledboolean—
general_config.storefront.review_auto_approval.auto_approve_ratingsarray—
general_config.deliveryobject—
general_config.delivery.return_requestsobject—
general_config.delivery.return_requests.destination_modestring—
general_config.delivery.return_requests.default_delivery_service_idstring—
general_config.delivery.return_requests.auto_approvalboolean—
general_config.delivery.on_demand_auto_assignmentobject—
general_config.delivery.on_demand_auto_assignment.enabledboolean—
general_config.delivery.on_demand_auto_assignment.priority_checksobject—
general_config.delivery.on_demand_auto_assignment.priority_checks.driver_onlineboolean—
general_config.delivery.on_demand_auto_assignment.priority_checks.preferred_zoneboolean—
general_config.delivery.on_demand_auto_assignment.priority_checks.vehicle_capacityboolean—
general_config.delivery.on_demand_auto_assignment.priority_checks.available_in_zoneboolean—
general_config.delivery.on_demand_auto_assignment.max_retry_attemptsnumber—
general_config.delivery.on_demand_auto_assignment.retry_after_minutesnumber—
general_config.delivery.on_demand_auto_assignment.notify_admin_on_failureboolean—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/tenants/wizard?language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "tenant": {6    "name": {7      "en": "Acme Logistics",8      "ar": "",9      "ku": ""10    },11    "slug": "acme-logistics",12    "contact_info": {13      "email": "admin@acme.com",14      "phone": "+19876543210"15    },16    "logo_url": "https://..."17  },18  "admin_user": {19    "create": true,20    "password": "StrongPassword123!"21  },22  "modules": {23    "enabled_module_ids": [24      "uuid-1",25      "uuid-2"26    ]27  },28  "subscription": {29    "type": "MONTHLY",30    "amount": 99.99,31    "note_public": "",32    "note_internal": ""33  },34  "brand_settings": {35    "brand_name": {36      "en": "Acme",37      "ar": "",38      "ku": ""39    },40    "assets": {41      "logo": {42        "light": "url",43        "dark": "url"44      },45      "favicon": "url"46    },47    "colors": {48      "text": "#000000",49      "accent": "#FF9500",50      "primary": "#007AFF",51      "secondary": "#FFFFFF",52      "background": "#F5F5F5"53    },54    "configurations": {55      "font": "Roboto",56      "theme": "light",57      "animations": false,58      "rtl_support": false59    }60  },61  "general_config": {62    "unit_size": {63      "unit_length": 30,64      "unit_weight": 5,65      "weight_unit": "KG",66      "volumetric_divisor": 5000,67      "dimension_unit": "CM",68      "use_volumetric_weight": false69    },70    "location": {71      "latitude": 36.19,72      "longitude": 44.0173    },74    "currency": {75      "currency_code": "USD",76      "currency_name": "US Dollar",77      "currency_symbol": "$",78      "currency_mode": "code",79      "exchange_rate_to_system": 180    },81    "domestic_country": {82      "code": "CA",83      "name": "Canada",84      "dial_code": "+1"85    },86    "driver": {87      "mode": "percentage",88      "percentage": {89        "percentage_of_delivery_fee": 090      },91      "per_delivery": {92        "base_amount": 093      },94      "driver_can_self_assign": false,95      "require_accept_or_reject_for_assignments": false96    },97    "integration": {98      "sms_channel": {99        "selected_channel": "twilio"100      }101    },102    "warehouse": {103      "handoff_required": false104    },105    "express_shipping": {106      "customer_prefix_key": ""107    },108    "user_settings": {109      "offline_threshold_seconds": 300,110      "heartbeat_interval_seconds": 30111    },112    "mobile": {113      "show_tenant_list_page": false114    },115    "storefront": {116      "review_auto_approval": {117        "enabled": false,118        "auto_approve_ratings": []119      }120    },121    "delivery": {122      "return_requests": {123        "destination_mode": "pickup_location",124        "default_delivery_service_id": "",125        "auto_approval": true126      },127      "on_demand_auto_assignment": {128        "enabled": false,129        "priority_checks": {130          "driver_online": true,131          "preferred_zone": true,132          "vehicle_capacity": true,133          "available_in_zone": true134        },135        "max_retry_attempts": 3,136        "retry_after_minutes": 1,137        "notify_admin_on_failure": true138      }139    }140  }141}'

Response examples

201Created
Response body
json
1{2  "success": true,3  "tenant_id": "550e8400-e29b-41d4-a716-446655440000",4  "message": "Tenant created successfully"5}

Get Tenant​

Platform & Access / Tenants
GET/tenants/:tenantId

Retrieves detailed information about a specific tenant, including associated users and statistics.

  • System administrators can access any tenant

  • Regular users can only access tenants they are associated with

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

UUID of the tenant to retrieve

Query parameters

ParameterTypeDescription
languageOptionalstring

Language of the response Language code for localized response messages (e.g., 'en')

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}?language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200Success (200 OK)
Response body
json
1{2  "success": true,3  "message": "Tenant retrieved successfully",4  "tenant": {5    "id": "uuid",6    "name": {7      "en": "Example Tenant"8    },9    "slug": "example-tenant",10    "status": "active",11    "contact_info": {12      "email": "contact@example.com",13      "phone": "+1234567890"14    },15    "metadata": {},16    "created_at": "2023-01-01T00:00:00Z",17    "created_by": "uuid",18    "updated_at": "2023-01-01T00:00:00Z",19    "updated_by": "uuid",20    "tenant_users": [21      {22        "user_id": "uuid",23        "created_at": "2023-01-01T00:00:00Z",24        "user": {25          "id": "uuid",26          "email": "user@example.com"27        }28      }29    ],30    "created_by_user": {31      "id": "uuid",32      "email": "admin@example.com"33    },34    "updated_by_user": {35      "id": "uuid",36      "email": "admin@example.com"37    },38    "statistics": {39      "active_users": 540    }41  }42}

Update Tenant​

Platform & Access / Tenants
PUT/tenants/:tenantId

Update tenant details. Requires tenant admin or system admin access.

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
nameobject—
name.enstring—
statusstring—
contact_infoobject—
contact_info.emailstring—
Staging
Request
curl
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "name": {6    "en": "Updated Name"7  },8  "status": "active",9  "contact_info": {10    "email": "updated@tenant.com"11  }12}'

Response examples

No saved response example. Use Try it to inspect a staging response.

List Tenant Modules​

Platform & Access / Tenants
GET/tenants/:tenantId/modules

Retrieves a list of modules associated with a specific tenant. Requires authentication - user must be either a system administrator or a member of the specified tenant.

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

The unique identifier of the tenant

Query parameters

ParameterTypeDescription
pagestring

Page number for pagination

Example: 1

limitstring

Number of results per page

Example: 10

languagestring

Language code for response messages

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/modules?page=1&limit=10&language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "message": "Tenant modules retrieved successfully",4  "tenantModules": [5    {6      "id": "uuid",7      "tenant_id": "tenant-uuid",8      "system_modules": {9        "name": "module-name",10        "description": "Module description",11        "version": "1.0.0",12        "dependencies": {},13        "features": []14      },15      "created_at": "timestamp",16      "updated_at": "timestamp"17    }18  ],19  "total": 10,20  "page": 1,21  "limit": 1022}

Enable Tenant Module​

Platform & Access / Tenants
POST/tenants/:tenantId/modules/:moduleId

Enable a module for a specific tenant. This endpoint allows system administrators to enable modules for any tenant, while tenant administrators can only enable modules for their own tenant if they have the 'manage:tenant' permission.

  • User must be either a system administrator or a tenant administrator with 'manage:tenant' permission

Validations

  • The module must be active at the system level

  • The module must be enabled at the system level

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

moduleIdRequiredstring

ID of the module to enable

Query parameters

ParameterTypeDescription
languageOptionalstring

Language code for response localization (Optional, default: en) Language code for localized response messages (e.g., 'en')

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/modules/${MODULEID}?language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200Success Response
Response body
json
1{2  "success": true,3  "message": "Module enabled successfully",4  "tenantModule": {5    "tenant_id": "550e8400-e29b-41d4-a716-446655440000",6    "module_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",7    "is_enabled": true,8    "enabled_at": "2023-01-01T00:00:00Z",9    "enabled_by": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",10    "updated_at": "2023-01-01T00:00:00Z",11    "updated_by": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"12  }13}

Disable Tenant Module​

Platform & Access / Tenants
DELETE/tenants/:tenantId/modules/:moduleId

Disable a module for a specific tenant. This endpoint allows system administrators to disable modules for any tenant, while tenant administrators can only disable modules for their own tenant if they have the 'manage:tenant' permission.

  • User must be either a system administrator or a tenant administrator with 'manage:tenant' permission

Validations

  • The module must be active at the system level

  • The module must be enabled at the system level

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

moduleIdRequiredstring

ID of the module to disable

Query parameters

ParameterTypeDescription
languageOptionalstring

Language code for response localization (Optional, default: en) Language code for localized response messages (e.g., 'en')

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request DELETE "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/modules/${MODULEID}?language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200Success Response
Response body
json
1{2  "success": true,3  "message": "Module enabled successfully",4  "tenantModule": {5    "tenant_id": "550e8400-e29b-41d4-a716-446655440000",6    "module_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",7    "is_enabled": false,8    "enabled_at": "2023-01-01T00:00:00Z",9    "enabled_by": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",10    "updated_at": "2023-01-01T00:00:00Z",11    "updated_by": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"12  }13}

Add User to Tenant​

Platform & Access / Tenants
POST/tenants/:tenantId/users

Add a user to a tenant. Requires tenant admin or system admin access.

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

Query parameters

ParameterTypeDescription
languagestring

Language of the response

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
user_idstring—
tenant_user_typestring—
is_primaryboolean—
metadataobject—
invite_tokenstring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/users?language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "user_id": "user-uuid",6  "tenant_user_type": "individual",7  "is_primary": false,8  "metadata": {},9  "invite_token": "optional-invite-token"10}'

Response examples

No saved response example. Use Try it to inspect a staging response.

Create API Key​

Platform & Access / Tenants
POST/tenants/:tenantId/api-keys

Create a new API key for the tenant. Returns the API key only once.

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

Query parameters

ParameterTypeDescription
languagestring

Language of the response

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
namestring—
user_idstring—
expires_atstring—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/api-keys?language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "name": "API Key Name",6  "user_id": "user-uuid",7  "expires_at": "2025-12-31T23:59:59Z"8}'

Response examples

No saved response example. Use Try it to inspect a staging response.

List API Keys​

Platform & Access / Tenants
GET/tenants/:tenantId/api-keys

List all API keys for the tenant with pagination

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

Query parameters

ParameterTypeDescription
pagestring

Page number

Example: 1

limitstring

Items per page

Example: 10

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/api-keys?page=1&limit=10&language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

No saved response example. Use Try it to inspect a staging response.

Revoke API Key​

Platform & Access / Tenants
DELETE/tenants/:tenantId/api-keys/:apiKeyId

Revoke an API key

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

apiKeyIdRequiredstring

ID of the API key to revoke

Query parameters

ParameterTypeDescription
languagestring

Language of the response

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request DELETE "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/api-keys/${APIKEYID}?language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

No saved response example. Use Try it to inspect a staging response.

Update API Key​

Platform & Access / Tenants
PUT/tenants/:tenantId/api-keys/:apiKeyId

Update an API key's properties

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

apiKeyIdRequiredstring

ID of the API key to update

Query parameters

ParameterTypeDescription
languagestring

Language of the response

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
namestring—
expires_atstring—
is_activeboolean—
Staging
Request
curl
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/api-keys/${APIKEYID}?language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "name": "Updated Name",6  "expires_at": "2025-12-31T23:59:59Z",7  "is_active": true8}'

Response examples

No saved response example. Use Try it to inspect a staging response.

List Tenant Addresses​

Platform & Access / Tenants
GET/tenants/:tenantId/addresses

List all active addresses for a tenant

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

Query parameters

ParameterTypeDescription
languagestring

Language of the response

Example: en

pagestring

Page number

Example: 1

limitstring

Number of addresses per page

Example: 10

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/addresses?language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

No saved response example. Use Try it to inspect a staging response.

Get Tenant Brand Settings​

Platform & Access / Tenants
GET/tenants/:tenantId/brand-settings

Read an active tenant’s brand name, assets, colors, configuration and color-palette preset. The authenticated tenant context takes precedence over a supplied tenant ID.

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

Query parameters

ParameterTypeDescription
languagestring

Language of the response

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/brand-settings?language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

No saved response example. Use Try it to inspect a staging response.

Update Tenant Brand Settings​

Platform & Access / Tenants
PUT/tenants/:tenantId/brand-settings

Update tenant brand settings. Requires system admin access.

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
brand_namestring—
assetsobject—
assets.logostring—
colorsobject—
colors.primarystring—
configurationsobject—
configurations.themestring—
Staging
Request
curl
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/brand-settings" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "brand_name": "Updated Brand Name",6  "assets": {7    "logo": "url-to-logo"8  },9  "colors": {10    "primary": "#000000"11  },12  "configurations": {13    "theme": "dark"14  }15}'

Response examples

No saved response example. Use Try it to inspect a staging response.

Remove User from Tenant​

Platform & Access / Tenants
DELETE/tenants/:tenantId/users/:userId

Remove a user from the tenant. Cannot remove the last admin.

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

userIdRequiredstring

ID of the user to remove

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request DELETE "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/users/${USERID}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

No saved response example. Use Try it to inspect a staging response.

Get Tenant Settings​

Platform & Access / Tenants
GET/tenants/:tenantId/settings

Get tenant settings. Optionally filter by specific keys. delivery.delivery_external_reference.settings.unique_per_customer controls whether a root delivery order's non-empty external_reference_id must be unique for the same customer in this tenant; it defaults to true. Staged child legs may reuse their root order's reference. general.timezone.settings.timezone is the tenant-wide IANA timezone used for local business times. general.unit_size.settings.missing_unit_size_fallback is the effective missing-unit policy; missing_unit_size_fallback_options contains all supported { value, label } choices localized for the request language. Route sessions are created explicitly and have no scheduling-enable tenant setting. If phone_login or domestic_country is returned, the response includes options.allowed_countries sourced from the system-level tenant phone-login allowlist so tenant admins can only choose countries permitted by the system. If 'currency' is present in any group, the response includes top-level 'currencies' with system and tenant context. If delivery.assignment_change_settings.settings.default_driver_id is set and the driver is still active, available, tenant-owned, and not deleted, the response also includes default_driver with display information (full_name, phone, avatar_url). Otherwise default_driver is null while the saved ID remains available so the admin UI can show a stale selection and clear it. If delivery.transfer_manifest is returned, settings.driver_scan_mode is always continuous or manual; missing or invalid stored values resolve to continuous. settings.allow_warehouse_staging_without_assigned_driver is always boolean and defaults to false; when true, unassigned Transfer Manifest P&D staging may use delivery.assignment_change_settings.settings.default_driver_id. Settings also include optional driver workflow booleans allow_driver_create_manifest_from_form, allow_driver_scan_institution_qr, and allow_driver_scan_item_barcode; at least one must be true. settings.delivery_fee_origin is processing_point (default) or source_location and controls the price basis for transfer-manifest-created orders without changing their processing-point pickup location. Its settings.reasons is the tenant-enabled array of { value, label, description } exception reasons. When not configured, allow_driver_create_manifest_from_form defaults to true and the scan toggles default to false. Warehouse package settings are returned under warehouse.package; auto_execute_delivery_order_on_receive controls whether fully received warehouse items auto-activate related draft delivery orders.

delivery.delivery_order.settings.edit.reason_required controls whether tenant-admin/dispatcher delivery-order edits require a reason. It defaults to false.

Frontend integration contract

read this effective tenant value to decide whether the edit form must require Reason. Backend enforcement remains authoritative.

Wallet limits are separate effective settings in the wallet group: wallet_minimum_balance.settings.minimum_balance is Drivers Minimum Balance; customer_wallet_minimum_balance.settings.minimum_balance is Customer Minimum Balance. Tenant overrides fall back to the corresponding system value. Customer delivery payments, draft reservations, fee increases, transfers and manual debits use the customer key; driver wallet operations use the existing driver key. New customer defaults and existing tenant overrides start at their previous wallet limit to preserve behavior.

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

Query parameters

ParameterTypeDescription
keysOptionalstring

Optional: Comma-separated list of setting keys

Example: setting1,setting2

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/settings" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200Success - With currencies
Response body
json
1{2  "success": true,3  "message": "tenant_settings_retrieved_successfully",4  "tenantSettings": {5    "general": {6      "currency": {7        "settings": {8          "currency_code": "IQD",9          "currency_symbol": "د.ع",10          "currency_name": "Iraqi Dinar",11          "exchange_rate_to_system": 0.0007612        },13        "description": null14      },15      "phone_login": {16        "settings": {17          "allowed_countries": [18            {19              "code": "IN",20              "name": "India",21              "dial_code": "+91"22            }23          ],24          "default_country": {25            "code": "IN",26            "name": "India",27            "dial_code": "+91"28          }29        },30        "description": "Phone login controls (allowed countries + default country)",31        "options": {32          "allowed_countries": [33            {34              "code": "CA",35              "name": "Canada",36              "dial_code": "+1"37            },38            {39              "code": "IN",40              "name": "India",41              "dial_code": "+91"42            },43            {44              "code": "TR",45              "name": "Turkey",46              "dial_code": "+90"47            }48          ],49          "default_country": [50            {51              "code": "CA",52              "name": "Canada",53              "dial_code": "+1"54            },55            {56              "code": "IN",57              "name": "India",58              "dial_code": "+91"59            },60            {61              "code": "TR",62              "name": "Turkey",63              "dial_code": "+90"64            }65          ]66        }67      },68      "domestic_country": {69        "settings": {70          "code": "CA",71          "name": "Canada",72          "dial_code": "+1"73        },74        "description": "Domestic country configuration for tenant (default: Canada)",75        "options": {76          "allowed_countries": [77            {78              "code": "CA",79              "name": "Canada",80              "dial_code": "+1"81            },82            {83              "code": "IN",84              "name": "India",85              "dial_code": "+91"86            },87            {88              "code": "TR",89              "name": "Turkey",90              "dial_code": "+90"91            }92          ]93        }94      }95    },96    "delivery": {97      "delivery_external_reference": {98        "settings": {99          "unique_per_customer": true100        },101        "description": "Controls customer-scoped external reference uniqueness for root delivery orders."102      },103      "assignment_change_settings": {104        "settings": {105          "enabled": true,106          "allow_unassign_after_pickup": false,107          "driver_can_self_unassign": false,108          "driver_can_self_unassign_after_pickup": false,109          "default_driver_id": "driver-uuid-123",110          "default_driver": {111            "full_name": "John Driver",112            "phone": "+1234567890",113            "avatar_url": "https://example.com/avatar.jpg"114          }115        },116        "description": null117      },118      "transfer_manifest": {119        "settings": {120          "enabled": true,121          "receiving_mode": "scan_each_item",122          "allow_driver_select_processing_point": false,123          "allow_driver_create_manifest_from_form": true,124          "allow_driver_scan_institution_qr": false,125          "allow_driver_scan_item_barcode": false,126          "allow_warehouse_staging_without_assigned_driver": false,127          "default_delivery_service_id": null,128          "default_delivery_service": null129        },130        "description": "Controls G2G transfer manifest pickup, receiving, processing, and default delivery service behavior for institution branch shipments."131      }132    },133    "warehouse": {134      "package": {135        "settings": {136          "handoff_required": false,137          "auto_execute_delivery_order_on_receive": false138        },139        "description": "Warehouse package handling settings. auto_execute_delivery_order_on_receive controls whether a related draft delivery order is automatically moved to pending after its warehouse item is fully received."140      }141    }142  },143  "currencies": {144    "system": {145      "currency_code": "USD",146      "currency_symbol": "$",147      "currency_name": "US Dollar"148    },149    "tenant": {150      "currency_code": "IQD",151      "currency_symbol": "د.ع",152      "currency_name": "Iraqi Dinar",153      "exchange_rate_to_system": 0.00076154    }155  }156}

Update Tenant Setting​

Platform & Access / Tenants
PUT/tenants/:tenantId/settings/:settingKey

Update a specific tenant setting. If settingKey=unit_size, send settingValue.missing_unit_size_fallback=service_min; allowed values are service_min, service_half, service_max, and error. The update preserves unit calculation fields and the response returns the canonical localized option labels.

If settingKey='delivery_external_reference', send { "settingValue": { "unique_per_customer": true } }. The field is required and must be boolean. true blocks a duplicate non-empty external_reference_id for the same customer and tenant at the root delivery-order level; false allows duplicates. The system default is true, and staged child legs may reuse their root order's reference. Unknown fields return HTTP 400 validation_error.

If settingKey='timezone', send { "settingValue": { "timezone": "Asia/Baghdad" } }. The value must be a valid IANA timezone. Route sessions are created explicitly; timezone changes do not create or reschedule them. Invalid values return HTTP 400 with details[].message = must_be_valid_iana_timezone.

If settingKey='currency' and tenant currency_code differs from system currency_code, you must provide a positive 'exchange_rate_to_system'. If codes match, exchange_rate_to_system is normalized to 1.

If settingKey='phone_login':

  • settingValue.allowed_countries can be an array of { code, name, dial_code } OR null.

  • When array is provided, it must be a subset of system-level phone_login.allowed_countries.

  • settingValue.default_country must be null or one of the allowed countries.

  • When allowed_countries is null, tenant falls back to system phone_login setting.

If settingKey='package', settingValue.handoff_required and settingValue.auto_execute_delivery_order_on_receive must be booleans when provided. auto_execute_delivery_order_on_receive=false is the default and keeps warehouse receiving behavior unchanged. When true, fully received warehouse items with related draft delivery orders are auto-executed to pending by backend receive logic.

If settingKey='assignment_change_settings', settingValue.default_driver_id accepts a tenant-owned active and available driver UUID or null to clear it. The update is merged with the existing setting, so saving the driver does not erase sibling assignment controls. Missing, deleted, inactive, unavailable, and cross-tenant drivers are rejected with localized driver errors.

If settingKey='transfer_manifest', settingValue.driver_scan_mode accepts only continuous or manual; other values return HTTP 400 validation_error with details[].message = must_be_continuous_or_manual. settingValue.allow_warehouse_staging_without_assigned_driver is an optional boolean and defaults to false; non-boolean values return HTTP 400 validation_error. Send settingValue.exception_reasons as an array of canonical reason strings. To select the fee basis, send settingValue.delivery_fee_origin as processing_point or source_location; for example { "settingValue": { "delivery_fee_origin": "source_location" } }. source_location prices promoted orders from the manifest source address while their saved pickup remains the processing point. Any other value returns HTTP 400 with details[].message = must_be_processing_point_or_source_location. The GET settings response exposes exception values as settings.reasons, an array of { value, label, description } objects. settingValue may also include optional booleans allow_driver_create_manifest_from_form, allow_driver_scan_institution_qr, and allow_driver_scan_item_barcode. When provided, these fields must be booleans or the API returns HTTP 400 with validation_error and details[].message = must_be_boolean. If one or more driver workflow fields are submitted, at least one must be true or the API returns HTTP 400 with details[].message = at_least_one_driver_manifest_entry_mode_required. Omit a key to preserve/fallback through the stored setting object used by the frontend contract.

If settingKey=delivery_order, send { "settingValue": { "edit": { "reason_required": true } } }. reason_required is mandatory and must be boolean. The backend deep-merges the edit section with existing effective delivery-order settings so future sibling sections are preserved. Unknown sections or edit fields return HTTP 400 validation_error.

Frontend integration contract

save the toggle through this endpoint and use the returned tenant setting as the persisted value.

For settingKey=customer_wallet_minimum_balance or settingKey=wallet_minimum_balance, send { "settingValue": { "minimum_balance": -1000000 } }. The customer key controls customer wallet balance checks; the existing key is Drivers Minimum Balance only. Both require a finite JSON number and reject unknown fields. Negative values allow debt down to the configured limit; zero allows no debt. Frontend integration: show both fields under Wallet Settings and save each using its own key.

Path parameters

ParameterTypeDescription
tenantIdRequiredstring

ID of the tenant

settingKeyRequiredstring

Key of the setting to update

Example: delivery_external_reference

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
settingValueobject—
settingValue.unique_per_customerboolean—
Staging
Request
curl
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/settings/${SETTINGKEY}" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "settingValue": {6    "unique_per_customer": false7  }8}'

Response examples

200Exception types updated
Response body
json
1{2  "success": true,3  "message": "Tenant setting updated successfully",4  "tenantSetting": {5    "tenant_id": "tenant-uuid",6    "setting_key": "exception_types",7    "setting_type": "delivery",8    "setting_value": {9      "types": [10        {11          "code": "customer_refused",12          "label": "Customer refused",13          "allowed_statuses": [14            "pending",15            "assigned",16            "en_route_to_pickup",17            "in_transit"18          ],19          "action_keys": [20            "retry",21            "return_to_sender",22            "cancel"23          ]24        }25      ]26    }27  }28}

Retry/Regenerate Label​

Platform & Access / Tenants
POST/tenants/labels/retry

Retry or regenerate a label for a delivery, inventory item, or delivery package item. This endpoint allows tenant administrators to trigger label regeneration regardless of the previous status.

  • User must be either a system administrator or a tenant administrator with 'manage:tenant' permission

At least one of the following IDs must be provided

  • delivery_id (optional): UUID of the delivery to regenerate label for

  • item_id (optional): UUID of the inventory item to regenerate label for

  • delivery_package_item_id (optional): UUID of the delivery package item to regenerate label for

Validations

  • At least one ID must be provided

  • The entity must belong to the tenant (for tenant admins)

  • System admins can regenerate labels for any tenant

Label Generation Failed (500 Internal Server Error)

Concurrent delivery-label regeneration

Manual retry still deliberately replaces the observed successful label version. If generation is already active, it is not superseded: HTTP 503 returns code=label_generation_in_progress with a localized retry message. Inventory-item and package-item behavior is unchanged.

Query parameters

ParameterTypeDescription
languageOptionalstring

Language code for response localization (Optional, default: en) Language code for localized response messages (e.g., 'en')

Example: en

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
delivery_idstring—
item_idnull—
delivery_package_item_idnull—
Staging
Request
curl
1curl --globoff --request POST "https://staging-api.ondi.io/functions/v1/tenants/labels/retry?language=en" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "delivery_id": "delivery-uuid",6  "item_id": null,7  "delivery_package_item_id": null8}'
Other request examples (3)
Request example 1
json
1{2  "delivery_id": "550e8400-e29b-41d4-a716-446655440000"3}
Request example 2
json
1{2  "item_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"3}
Request example 3
json
1{2  "delivery_package_item_id": "98ec3d92-82f8-44ff-a0bd-9f76bad284d7"3}

Response examples

200Success - Label Regenerated
Response body
json
1{2  "success": true,3  "message": "Label regenerated successfully",4  "url": "https://example.supabase.co/storage/v1/object/public/shipment-label-pdfs/delivery/shipment-labels/550e8400-e29b-41d4-a716-446655440000-2.pdf"5}

Get My Subscription​

Platform & Access / Tenants
GET/tenants/me/subscription

Returns the authenticated tenant subscription. Response content type is always application/json.

Response

  • success: boolean

  • subscription_enabled: boolean - whether the tenant subscription model is enabled globally (system setting)

  • data: subscription object

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/tenants/me/subscription" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

200OK
Response body
json
1{2  "success": true,3  "subscription_enabled": true,4  "data": {}5}

Get My Subscription History​

Platform & Access / Tenants
GET/tenants/me/subscription/history

Returns the authenticated tenant subscription history. Response content type is always application/json.

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Staging
Request
curl
1curl --globoff --request GET "https://staging-api.ondi.io/functions/v1/tenants/me/subscription/history" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}"

Response examples

No saved response example. Use Try it to inspect a staging response.

Update G2G Available Services Setting​

Platform & Access / Tenants
PUT/tenants/:tenantId/settings/transfer_manifest

Tenant-admin contract for Available services for G2G. available_delivery_service_ids must be an array of active delivery-service UUIDs owned by the tenant. Missing or [] means no additional G2G limitation. When the list is non-empty, default_delivery_service_id must be included. The backend deduplicates IDs and rejects inactive, missing, or cross-tenant services.

GET contract

GET /tenants/:tenantId/settings?keys=transfer_manifest returns normalized settings.available_delivery_service_ids, settings.available_delivery_services as [{ "id": string, "name": string, "code": string, "image": string | null, "delivery_type": { "id": string, "name": string, "code": string }, "active": boolean }], and the existing default_delivery_service.

Frontend integration contract

populate the settings multi-select from GET /delivery/services?active=true&include_stats=false without G2G context. Save selected IDs here. Operational G2G pickers use GET /delivery/services?context=g2g&active=true&include_stats=false. Handle HTTP 400 codes g2g_available_delivery_services_invalid and g2g_default_delivery_service_must_be_available.

Path parameters

ParameterTypeDescription
tenantIdRequiredstring—

Headers

ParameterTypeDescription
Authorizationstring

Example: Bearer {{access_token}}

Content-Typestring

Example: application/json

Body parameters

ParameterTypeDescription
settingValueobject—
settingValue.default_delivery_service_idstring—
settingValue.available_delivery_service_idsarray<string>—
Staging
Request
curl
1curl --globoff --request PUT "https://staging-api.ondi.io/functions/v1/tenants/${TENANTID}/settings/transfer_manifest" \2  --header "Authorization: Bearer ${ONDI_ACCESS_TOKEN}" \3  --header "Content-Type: application/json" \4  --data-raw '{5  "settingValue": {6    "default_delivery_service_id": "{{delivery_service_id}}",7    "available_delivery_service_ids": [8      "{{delivery_service_id}}"9    ]10  }11}'

Replace {{placeholders}} with your own values before sending.

Response examples

200G2G service setting updated
Response body
json
1{2  "success": true,3  "message": "Tenant setting updated successfully",4  "tenantSetting": {5    "setting_key": "transfer_manifest",6    "setting_value": {7      "default_delivery_service_id": "{{delivery_service_id}}",8      "available_delivery_service_ids": [9        "{{delivery_service_id}}"10      ]11    }12  }13}