Home Integration API reference
REST API for integrations and external apps
Team management
Team management endpoints cover staff users, business roles, and sales commission agents. They use the same permission checks as the web Team management area (Users, Roles, and commission agents).
Lists staff users for the current business. Commission agents are excluded from this endpoint.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/users |
| Authentication | Bearer token or API key + secret required. |
| Permission | user.view or user.create |
| Scope | Only user_type = user rows with is_cmmsn_agnt = 0 are returned. |
| CSV behavior | format=csv streams all matching rows with a UTF-8 BOM and ignores pagination. |
| Success response | 200 with paginated UserListItem rows. |
| Parameter | Type | Required | Description |
|---|---|---|---|
per_page | integer | No | Results per page from 1 to 100. Defaults to 20. |
page | integer | No | Pagination page number. |
role_id | integer | No | Filter by a business role id. |
status | string | No | active, inactive, 1, or 0. |
q | string | No | Minimum 2 characters when sent. Matches full name, email, username, or contact number. |
search | string | No | Legacy free-text search alias used when q is absent. |
sort | string | No | name, username, email, status, or created_at. Defaults to created_at. |
direction | string | No | asc or desc. Defaults to desc. |
format | string | No | json (default) or csv. |
UserListItem object| Field | Type | Description |
|---|---|---|
id | integer | Staff user id. |
username | string | null | Login username when login is enabled. |
email | string | null | User email address. |
full_name | string | Display name assembled from surname, first name, and last name. |
contact_number | string | null | Contact number stored on the user record. |
status | string | active or inactive. |
allow_login | boolean | Whether the user can log in. |
role_name | string | null | Current role display name. |
created_at | string | null | ISO-8601 creation timestamp. |
| Field | Type | Description |
|---|---|---|
data | array<UserListItem> | Paginated staff user rows. |
meta.current_page, meta.last_page, meta.per_page, meta.total | integer | Pagination metadata. |
| Status | When it happens | Response shape |
|---|---|---|
200 | Users were returned successfully. | JSON { "data": [...], "meta": { ... } } or CSV download. |
403 | The token user lacks list access. | { "message": "Unauthorized" } |
422 | The query string failed validation, such as a 1-character q. | Laravel validation JSON or { "message": string }. |
500 | The list query or CSV export failed unexpectedly. | { "message": "Could not list users" } |
Returns one staff user from the current business.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/users/{id} |
| Permission | user.view |
| Scope | Same staff-user scope as List users. |
| CSV behavior | format=csv streams one row with a data_json column. |
| Success response | 200 with a UserDetail object. |
| Parameter | Type | Required | Description |
|---|---|---|---|
format | string | No | json (default) or csv. |
UserDetail object| Field | Type | Description |
|---|---|---|
id | integer | Staff user id. |
username | string | null | Login username. |
surname, first_name, last_name | string | null | Name fields stored on the user. |
full_name | string | Collapsed display name. |
email, contact_number | string | null | Primary communication fields. |
status | string | active or inactive. |
allow_login | boolean | Whether the user can log in. |
language | string | null | User language key. |
role_id | integer | null | Current role id. |
role_name | string | null | Current role display name. |
selected_contacts | boolean | Whether the user is limited to a selected contact list. |
contact_access | array<object> | Allowed contact rows. Each item contains id and name. |
permitted_locations.scope | string | all or locations. |
permitted_locations.location_ids | array<integer> | null | Location ids when the scope is locations. |
created_at, updated_at | string | null | ISO-8601 timestamps. |
| Field | Type | Description |
|---|---|---|
data | UserDetail | Detailed staff user payload. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The user was returned successfully. | { "data": UserDetail } or CSV download. |
403 | The token user lacks user.view. | { "message": "Unauthorized" } |
404 | The user id does not exist in the current business or is not a staff user. | { "message": "Not found" } |
Creates a new staff user using the same core workflow as the web Users screen.
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/v1/integration/users |
| Permission | user.create |
| Demo mode | Returns 403 in demo environments. |
| Subscription rule | Returns 402 when package enforcement is active and the business is not subscribed. |
| User quota | Checks the business user quota before creation and returns 422 when the quota is exhausted. |
| Request encoding | application/json |
| Success response | 201 with a UserDetail object. |
| Field | Type | Required | Description |
|---|---|---|---|
first_name | string | Yes | Primary user name field. |
surname, last_name | string | null | No | Optional additional name fields. |
email | string | null | No | Email address. |
role | integer | Yes | Role id belonging to the current business. |
allow_login | boolean | No | Whether the user should be login-capable. Defaults to false. |
username | string | null | No | Optional username. When login is enabled and this is blank, the platform generates one. |
password | string | null | Conditional | Required when allow_login is true. Minimum length is 6 characters. |
is_active | boolean | No | Defaults to true. |
location_access.scope | string | Yes | all or locations. |
location_access.location_ids | array<integer> | null | Conditional | Required when location_access.scope is locations. All ids must belong to the current business. |
selected_contacts | boolean | No | Whether the user should be limited to a chosen set of contacts. |
selected_contact_ids | array<integer> | null | No | Optional contact ids for the current business. |
| Status | When it happens | Response shape |
|---|---|---|
201 | The user was created successfully. | { "data": UserDetail } |
402 | The business is not subscribed when package enforcement is active. | { "message": string } |
403 | Demo mode is active or the token user lacks user.create. | { "message": string } |
422 | The payload failed validation, the user quota is exhausted, referenced locations/contacts do not belong to the business, or the create pipeline rejected the request. | Laravel validation JSON or { "message": string }. |
500 | The create pipeline returned an unexpected result. | { "message": "something_went_wrong" } |
Updates an existing staff user and replaces role, location access, and selected contact access using the same core workflow as the web edit form.
| Property | Value |
|---|---|
| Methods | PATCH or PUT |
| Path | /api/v1/integration/users/{id} |
| Permission | user.update |
| Demo mode | Returns 403 in demo environments. |
| Subscription rule | Returns 402 when package enforcement is active and the business is not subscribed. |
| Request encoding | application/json |
| Success response | 200 with the updated UserDetail object. |
| Field | Type | Required | Description |
|---|---|---|---|
role | integer | Yes | Replacement role id belonging to the current business. |
location_access.scope | string | Yes | all or locations. |
location_access.location_ids | array<integer> | null | Conditional | Required when location_access.scope is locations. |
surname, first_name, last_name, email | string | null | No | Optional profile fields. Omitted values keep their current values. |
allow_login | boolean | No | When false, the controller clears username and password and disables login. |
username | string | null | No | Optional replacement username when login remains enabled. |
password | string | null | No | Optional new password. Minimum length is 6 characters. |
is_active | boolean | No | Maps to the user's status field. |
selected_contacts | boolean | No | Whether the user should be limited to selected contacts. |
selected_contact_ids | array<integer> | null | No | Replacement selected contacts. Send an empty array to clear access when selected_contacts is true. |
| Rule | Description |
|---|---|
| User quota | The controller only checks quota when allow_login is sent truthy during the update. |
| Last admin protection | Changing the role of the last remaining admin user returns 422 with the same message used by the web app. |
| Role replacement | The user's previous role is removed and the new role is assigned when the role id changes. |
| Contact access sync | selected_contact_ids replaces the saved contact list when selected contact mode is enabled. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The user was updated successfully. | { "data": UserDetail } |
402 | The business is not subscribed when package enforcement is active. | { "message": string } |
403 | Demo mode is active or the token user lacks user.update. | { "message": string } |
404 | The user id does not exist in the current business or is not a staff user. | { "message": "Not found" } |
422 | The payload failed validation, the login quota check failed, a location/contact id is outside the business, or a business rule such as last-admin protection blocked the update. | Laravel validation JSON or { "message": string }. |
Deletes a staff user from the current business.
| Property | Value |
|---|---|
| Method | DELETE |
| Path | /api/v1/integration/users/{id} |
| Permission | user.delete |
| Demo mode | Returns 403 in demo environments. |
| Self-delete rule | The authenticated user cannot delete their own account. |
| Success response | 200 with the deleted user id. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The user was deleted successfully. | { "message": string, "data": { "id": integer } } |
403 | Demo mode is active or the token user lacks user.delete. | { "message": string } |
404 | The user id does not exist in the current business or is not a staff user. | { "message": "Not found" } |
422 | The authenticated user attempted to delete their own account. | { "message": "Cannot delete your own user account" } |
500 | The delete transaction failed unexpectedly. | { "message": "something_went_wrong" } |
Lists roles for the authenticated business, using the same role rows shown in the web Roles screen.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/roles |
| Authentication | Bearer token or API key + secret required. |
| Permission | roles.view |
| CSV behavior | format=csv streams all matching rows with a UTF-8 BOM and ignores pagination. |
| Success response | 200 with paginated RoleListItem rows. |
| Parameter | Type | Required | Description |
|---|---|---|---|
per_page | integer | No | Results per page from 1 to 100. Defaults to 20. |
page | integer | No | Pagination page number. |
q | string | No | Minimum 2 characters when sent. Matches the stored role name. |
search | string | No | Legacy free-text search alias used when q is absent. |
sort | string | No | name, created_at, or users_count. Defaults to name. |
direction | string | No | asc or desc. Defaults to asc. |
format | string | No | json (default) or csv. |
RoleListItem object| Field | Type | Description |
|---|---|---|
id | integer | Role id. |
name | string | Display name shown in the UI. Built-in Admin and Cashier labels are translated. |
name_key | string | Role key with the business suffix removed. |
name_storage | string | Stored database name including the #business_id suffix. |
is_default | boolean | Whether the role is marked as a default role. |
is_service_staff | boolean | Whether the role is flagged as service staff. |
users_count | integer | Number of business users currently assigned to the role. |
created_at | string | null | ISO-8601 creation timestamp. |
| Field | Type | Description |
|---|---|---|
data | array<RoleListItem> | Paginated role rows. |
meta.current_page, meta.last_page, meta.per_page, meta.total | integer | Pagination metadata. |
| Status | When it happens | Response shape |
|---|---|---|
200 | Roles were returned successfully. | JSON { "data": [...], "meta": { ... } } or CSV download. |
403 | The token user lacks roles.view. | { "message": "Unauthorized" } |
422 | The query string failed validation, such as a 1-character q. | Laravel validation JSON or { "message": string }. |
500 | The role query or CSV export failed unexpectedly. | { "message": "Could not list roles" } |
Returns one role with its sorted permission names.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/roles/{id} |
| Permission | roles.view |
| CSV behavior | format=csv streams one row with a data_json column. |
| Success response | 200 with a RoleDetail object. |
| Parameter | Type | Required | Description |
|---|---|---|---|
format | string | No | json (default) or csv. |
RoleDetail object| Field | Type | Description |
|---|---|---|
id | integer | Role id. |
name, name_key, name_storage | string | Display, suffix-stripped, and stored role names. |
guard_name | string | Spatie guard name. |
is_default, is_service_staff | boolean | Role flags. |
users_count | integer | Number of business users assigned to the role. |
permissions | array<string> | Sorted permission names attached to the role. |
created_at, updated_at | string | null | ISO-8601 timestamps. |
| Field | Type | Description |
|---|---|---|
data | RoleDetail | Detailed role payload. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The role was returned successfully. | { "data": RoleDetail } or CSV download. |
403 | The token user lacks roles.view. | { "message": "Unauthorized" } |
404 | The role id does not exist in the current business. | { "message": "Not found" } |
Creates a role for the current business, including optional permission sets and service-staff flags.
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/v1/integration/roles |
| Permission | roles.create |
| Demo mode | Returns 403 in demo environments. |
| Request encoding | application/json |
| Success response | 201 with a RoleDetail object. |
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name without the #business_id suffix. The name must not contain #. |
permissions | array<string> | null | No | Base permission names to attach. |
is_service_staff | boolean | No | Marks the role as service staff. |
spg_permissions | array<string> | null | No | Additional permission names merged into permissions. |
radio_options | array<string> | null | No | Additional permission names merged into permissions. |
| Rule | Description |
|---|---|
| Merged sources | The controller merges permissions, spg_permissions, and radio_options into one unique permission list. |
| Unknown permissions | Missing permission records are created automatically before the role is synced. |
| Stored name | The saved role name is the display name plus #business_id. |
| Status | When it happens | Response shape |
|---|---|---|
201 | The role was created successfully. | { "data": RoleDetail } |
403 | Demo mode is active or the token user lacks roles.create. | { "message": string } |
422 | The payload failed validation, the name contains #, or a role with the same stored name already exists. | Laravel validation JSON or { "message": string }. |
500 | The create transaction failed unexpectedly. | { "message": "something_went_wrong" } |
Updates a role and replaces its permissions with the merged permission list from the request payload.
| Property | Value |
|---|---|
| Methods | PATCH or PUT |
| Path | /api/v1/integration/roles/{id} |
| Permission | roles.update |
| Demo mode | Returns 403 in demo environments. |
| Default-role rule | Default roles cannot be edited unless the role is the built-in Cashier role for this business. |
| Request encoding | application/json |
| Success response | 200 with the updated RoleDetail object. |
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Replacement display name without #. |
permissions | array<string> | null | No | Replacement base permission list. |
is_service_staff | boolean | No | Replacement service-staff flag. |
spg_permissions | array<string> | null | No | Additional permission names merged into the final permission set. |
radio_options | array<string> | null | No | Additional permission names merged into the final permission set. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The role was updated successfully. | { "data": RoleDetail } |
403 | Demo mode is active or the token user lacks roles.update. | { "message": string } |
404 | The role id does not exist in the current business. | { "message": "Not found" } |
422 | The payload failed validation, the name is invalid or duplicated, or a protected default role was targeted. | Laravel validation JSON or { "message": string }. |
500 | The update transaction failed unexpectedly. | { "message": "something_went_wrong" } |
Deletes a role from the current business.
| Property | Value |
|---|---|
| Method | DELETE |
| Path | /api/v1/integration/roles/{id} |
| Permission | roles.delete |
| Demo mode | Returns 403 in demo environments. |
| Default-role rule | Default roles cannot be deleted unless the role is the built-in Cashier role for this business. |
| Success response | 200 with the deleted role id. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The role was deleted successfully. | { "message": string, "data": { "id": integer } } |
403 | Demo mode is active or the token user lacks roles.delete. | { "message": string } |
404 | The role id does not exist in the current business. | { "message": "Not found" } |
422 | A protected default role other than Cashier was targeted. | { "message": string } |
500 | The delete transaction failed unexpectedly. | { "message": "something_went_wrong" } |
Lists sales commission agents for the authenticated business.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/commission-agents |
| Authentication | Bearer token or API key + secret required. |
| Permission | user.view or user.create |
| Scope | Only user_type = user rows with is_cmmsn_agnt = 1 are returned. |
| CSV behavior | format=csv streams all matching rows with a UTF-8 BOM and ignores pagination. |
| Success response | 200 with paginated CommissionAgentListItem rows. |
| Parameter | Type | Required | Description |
|---|---|---|---|
per_page | integer | No | Results per page from 1 to 100. Defaults to 20. |
page | integer | No | Pagination page number. |
status | string | No | active, inactive, or terminated. |
q | string | No | Minimum 2 characters when sent. Matches full name, email, or contact number. |
search | string | No | Legacy search alias used when q is absent. |
sort | string | No | name, email, cmmsn_percent, created_at, or status. Defaults to created_at. |
direction | string | No | asc or desc. Defaults to desc. |
format | string | No | json (default) or csv. |
CommissionAgentListItem object| Field | Type | Description |
|---|---|---|
id | integer | Commission agent id. |
full_name | string | Collapsed display name. |
email | string | null | Email address. |
contact_no | string | null | Contact number. |
address | string | null | Address text. |
cmmsn_percent | number | null | Commission percentage. |
status | string | Agent status. |
created_at | string | null | ISO-8601 creation timestamp. |
| Field | Type | Description |
|---|---|---|
data | array<CommissionAgentListItem> | Paginated commission agent rows. |
meta.current_page, meta.last_page, meta.per_page, meta.total | integer | Pagination metadata. |
| Status | When it happens | Response shape |
|---|---|---|
200 | Commission agents were returned successfully. | JSON { "data": [...], "meta": { ... } } or CSV download. |
403 | The token user lacks access to the commission-agent list. | { "message": "Unauthorized" } |
422 | The query string failed validation, such as a 1-character q. | Laravel validation JSON or { "message": string }. |
500 | The agent query or CSV export failed unexpectedly. | { "message": "Could not list commission agents" } |
Returns one sales commission agent from the current business.
| Property | Value |
|---|---|
| Method | GET |
| Path | /api/v1/integration/commission-agents/{id} |
| Permission | user.view or user.create |
| CSV behavior | format=csv streams one row with a data_json column. |
| Success response | 200 with a CommissionAgentDetail object. |
| Parameter | Type | Required | Description |
|---|---|---|---|
format | string | No | json (default) or csv. |
CommissionAgentDetail object| Field | Type | Description |
|---|---|---|
id | integer | Commission agent id. |
surname, first_name, last_name | string | null | Name fields stored on the user record. |
full_name | string | Collapsed display name. |
email, contact_no, address | string | null | Communication fields. |
cmmsn_percent | number | null | Commission percentage. |
status | string | Agent status. |
allow_login | boolean | Always false for agents created by this API flow. |
is_commission_agent | boolean | Always true for this endpoint. |
created_at, updated_at | string | null | ISO-8601 timestamps. |
| Field | Type | Description |
|---|---|---|
data | CommissionAgentDetail | Detailed commission agent payload. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The commission agent was returned successfully. | { "data": CommissionAgentDetail } or CSV download. |
403 | The token user lacks access to the commission-agent detail endpoint. | { "message": "Unauthorized" } |
404 | The commission agent id does not exist in the current business. | { "message": "Not found" } |
Creates a sales commission agent in the current business.
| Property | Value |
|---|---|
| Method | POST |
| Path | /api/v1/integration/commission-agents |
| Permission | user.create |
| Request encoding | application/json |
| Created defaults | The controller sets user_type = user, allow_login = 0, is_cmmsn_agnt = 1, and status = active. |
| Success response | 201 with a CommissionAgentDetail object and success message. |
| Field | Type | Required | Description |
|---|---|---|---|
first_name | string | Yes | Primary commission agent name field. |
cmmsn_percent | number | Yes | Commission percentage. Parsed with the same numeric normalizer as the web form. |
surname, last_name | string | null | No | Optional additional name fields. |
email | string | null | No | Email address. |
contact_no | string | null | No | Contact number. |
address | string | null | No | Address text. |
language | string | null | No | Optional language key. Defaults to en when omitted. |
| Status | When it happens | Response shape |
|---|---|---|
201 | The commission agent was created successfully. | { "message": "Created", "data": CommissionAgentDetail } |
403 | The token user lacks user.create. | { "message": "Unauthorized" } |
422 | The payload failed validation. | Laravel validation JSON. |
500 | The create transaction failed unexpectedly. | { "message": "Could not create commission agent" } |
Updates a sales commission agent. The endpoint supports partial updates.
| Property | Value |
|---|---|
| Methods | PATCH or PUT |
| Path | /api/v1/integration/commission-agents/{id} |
| Permission | user.update |
| Request encoding | application/json |
| Success response | 200 with the updated CommissionAgentDetail object and success message. |
| Field | Type | Required | Description |
|---|---|---|---|
surname, first_name, last_name | string | null | No | Optional updated name fields. |
email | string | null | No | Updated email address. |
contact_no | string | null | No | Updated contact number. |
address | string | null | No | Updated address text. |
cmmsn_percent | number | null | No | Updated commission percentage. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The commission agent was updated successfully. | { "message": "Updated", "data": CommissionAgentDetail } |
403 | The token user lacks user.update. | { "message": "Unauthorized" } |
404 | The commission agent id does not exist in the current business. | { "message": "Not found" } |
422 | The payload failed validation. | Laravel validation JSON. |
500 | The update transaction failed unexpectedly. | { "message": "Could not update commission agent" } |
Deletes a sales commission agent from the current business.
| Property | Value |
|---|---|
| Method | DELETE |
| Path | /api/v1/integration/commission-agents/{id} |
| Permission | user.delete |
| Delete behavior | Uses the model's normal delete behavior, including soft delete when enabled on User. |
| Success response | 200 with the deleted commission agent id. |
| Status | When it happens | Response shape |
|---|---|---|
200 | The commission agent was deleted successfully. | { "message": "Deleted", "data": { "id": integer } } |
403 | The token user lacks user.delete. | { "message": "Unauthorized" } |
404 | The commission agent id does not exist in the current business. | { "message": "Not found" } |
500 | The delete transaction failed unexpectedly. | { "message": "Could not delete commission agent" } |