Public Data API
Action-based API for reading and writing Total Access data from external applications.
Public Data API
The Public Data API provides a simplified, action-based endpoint for external applications to read and write Total Access data using an API key. It uses the X-API-Key header instead of Authorization: Bearer.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/v1/public/data | Discovery and documentation |
POST | /api/v1/public/data | Execute an action |
GET | /api/v1/public/data/tools?format=mcp | Tool catalog for AI agents (MCP schema) |
GET | /api/v1/public/data/tools?format=openai | Tool catalog (OpenAI function schema) |
POST | /api/v1/public/mcp | MCP JSON-RPC adapter |
See AI Agent & MCP Bridge for agent-specific usage.
Authentication
X-API-Key: ta_sk_a1b2c3d4e5f6...The API key must have the appropriate scopes for the action being performed. See Authentication for the scope catalog.
Request format
All requests are POST with a JSON body containing an action field:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_a1b2c3d4e5f6..." \
-H "Content-Type: application/json" \
-d '{
"action": "get-active-users",
"status": "active"
}'Available actions
Users & Employees
| Action | Required Scope | Description |
|---|---|---|
get-active-users | users:read | Get all active users with client membership |
get-employees | employees:read | Get employees with pagination and filters |
get-user-memberships | users:read | Get user-client membership details |
get-subordinates | employees:read or users:read | Get employees reporting to a manager (direct reports and/or all descendants) |
Example — Get employees:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "get-employees",
"is_active": true,
"include_terminated": false,
"limit": 100,
"offset": 0
}'Example — Get subordinates (reporting hierarchy):
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "get-subordinates",
"user_id": 88,
"include_descendants": true,
"include_self": true
}'get-subordinates parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
employee_id | number | No* | Employee ID of the manager to get reports for |
user_id | number | No* | User ID of the manager (resolved to employee_id if not provided) |
include_descendants | boolean | No | Include all descendants in the reporting tree, not just direct reports (default: false) |
include_self | boolean | No | Include the manager themselves in the results (default: false) |
* Either employee_id or user_id is required.
The response includes each employee's depth (0 = self, 1 = direct report, 2 = report of report, etc.) and a scope object with employee_ids and user_ids arrays for easy use in downstream queries.
Work Items
| Action | Required Scope | Description |
|---|---|---|
get-work-item | work_items:read | Get a specific work item by ID |
list-work-items | work_items:read | List work items with filters |
get-work-item-relationships | work_items:read | Get relationships for a work item |
get-work-item-attachments | work_items:read | Get attachments for a work item |
get-work-item-comments | work_items:read | Get comments for a work item |
get-work-item-time-entries | work_items:read | Get time entries for a work item |
create-work-item | work_items:write | Create a new work item (job, task, ticket, etc.) |
update-work-item | work_items:write | Update an existing work item |
close-work-item | work_items:write | Mark a work item as completed/closed/resolved/cancelled |
delete-work-item | work_items:write or work_items:delete | Soft-delete a work item |
create-appointment | appointments:write or work_items:write | Create a new appointment with scheduled start/end times |
create-comment | comments:write or work_items:write | Add a comment to a work item |
get-employee-due-tasks | work_items:read or employees:read | Get tasks due today (and optionally overdue) for a user |
get-employee-overdue-tasks | work_items:read or employees:read | Get overdue tasks for a user |
create-managed-task | work_items:write | Create a standalone managed task assigned to one or more users |
add-work-item-to-managed-task | work_items:write | Add any existing work item to a user's managed task list |
Example — Create a work item:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "create-work-item",
"item_type": "job",
"title": "Install new equipment",
"description": "Install and configure new server rack",
"module_context": "service-operations",
"status": "open",
"priority": "high",
"contact_id": 123,
"contact_name": "Acme Corporation",
"contact_email": "[email protected]",
"contact_phone": "+27123456789",
"location_id": 456,
"location_name": "Main Office",
"location_address": "123 Main Street, Johannesburg, 2000",
"contact_person_id": 789,
"contact_person": "Jane Doe",
"assigned_to": 150,
"assigned_users": [150, 151],
"scheduled_start": "2026-08-01T08:00:00Z",
"scheduled_end": "2026-08-01T12:00:00Z",
"due_date": "2026-08-01",
"estimated_hours": 4,
"tags": ["urgent", "installation"],
"custom_fields": {
"safety_requirements": "Hard hat required",
"equipment_required": "Drill, safety gear"
},
"notes": "Ensure power is isolated before starting"
}'create-work-item parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
item_type | string | Yes | job, task, ticket, project, milestone |
title | string | Yes | Work item title |
description | string | No | Work item description |
module_context | string | No | Workflow template key (defaults to service-operations for jobs) |
status | string | No | Initial status (defaults vary by item_type) |
priority | string | No | low, medium, high, urgent |
contact_id | number | No | Customer/contact ID |
contact_name | string | No | Contact display name |
contact_email | string | No | Contact email |
contact_phone | string | No | Contact phone |
location_id | number | No | Contact location ID |
location_name | string | No | Location name |
location_address | string | No | Location address |
location_coordinates | object | No | { lat: number, lng: number } |
contact_person_id | number | No | Site contact person ID |
contact_person | string | No | Site contact person name |
assigned_to | number | No | Primary assignee user ID |
assigned_team | string | No | Team UUID |
assigned_users | number[] | No | Multiple assignee user IDs |
assigned_contractors | number[] | No | Contractor contact IDs |
scheduled_start | string | No | ISO 8601 datetime |
scheduled_end | string | No | ISO 8601 datetime |
start_date | string | No | ISO 8601 datetime |
due_date | string | No | ISO 8601 datetime |
estimated_hours | number | No | Estimated effort in hours |
parent_id | string | No | Parent work item UUID |
project_id | string | No | Project work item UUID (creates child_of relationship) |
tags | string[] | No | Array of tag strings |
custom_fields | object | No | Arbitrary JSON object for custom data |
notes | string | No | Additional notes |
Example — Update a work item:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "update-work-item",
"work_item_id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Updated title — urgent repair needed",
"status": "in_progress",
"priority": "urgent",
"assigned_to": 150,
"assigned_users": [150, 151],
"due_date": "2026-08-05",
"tags": ["urgent", "repair"],
"custom_fields": {
"safety_requirements": "Hard hat and safety vest required"
}
}'update-work-item parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
work_item_id | string | Yes | UUID of the work item to update |
title | string | No | Updated title |
description | string | No | Updated description |
item_subtype | string | No | Updated subtype |
module_context | string | No | Updated module context |
status | string | No | Updated status (e.g. open, in_progress, completed, cancelled) |
priority | string | No | low, medium, high, urgent, critical |
assigned_to | number | No | Primary assignee user ID |
assigned_team | string | No | Team UUID |
assigned_users | number[] | No | Replaces existing user assignees |
assigned_contractors | number[] | No | Replaces existing contractor assignees |
contact_id | number | No | Contact ID |
contact_name | string | No | Contact name |
contact_email | string | No | Contact email |
contact_phone | string | No | Contact phone |
location_id | number | No | Contact location ID |
location_name | string | No | Location name |
location_address | string | No | Location address |
location_coordinates | object | No | { lat: number, lng: number } |
contact_person_id | number | No | Contact person ID |
contact_person | string | No | Contact person name |
reporter_id | number | No | Reporter user ID |
reporter_name | string | No | Reporter name |
reporter_email | string | No | Reporter email |
reporter_phone | string | No | Reporter phone |
start_date | string | No | ISO 8601 datetime |
due_date | string | No | ISO 8601 datetime |
scheduled_start | string | No | ISO 8601 datetime |
scheduled_end | string | No | ISO 8601 datetime |
estimated_hours | number | No | Estimated effort in hours |
tags | string[] | No | Updated tags array |
custom_fields | object | No | Updated custom fields object |
notes | string | No | Updated notes |
Example — Delete a work item (soft delete):
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "delete-work-item",
"work_item_id": "550e8400-e29b-41d4-a716-446655440000"
}'Soft delete behaviour
Deleting a work item sets deleted_at and deleted_by on the record — the item is not hard-deleted from the database. Child items (appointments, tasks linked via child_of relationships) are cascade-soft-deleted as well. The operation is idempotent: deleting an already-deleted item returns success.
delete-work-item parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
work_item_id | string | Yes | UUID of the work item to soft-delete |
Example — Close a work item:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "close-work-item",
"work_item_id": "550e8400-e29b-41d4-a716-446655440000",
"closure_status": "completed",
"completion_note": "Work verified by client"
}'close-work-item parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
work_item_id | string | Yes | UUID of the work item to close |
closure_status | string | No | completed (default), closed, done, resolved, cancelled, canceled |
completion_note | string | No | Optional note stored in the activity log |
Managed Tasks
Managed tasks are work items with item_type: "task" that appear in a user's "Managed Tasks" list. They can be standalone tasks or linked to other work items (jobs, invoices, appointments) via work_item_relationships. The following actions let AI agents and external integrations create, query, and assign managed tasks without requiring WhatsApp or manual UI interaction.
Example — Create a standalone managed task:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "create-managed-task",
"title": "Follow up with supplier",
"description": "Confirm delivery date for order #1234",
"priority": "high",
"due_date": "2026-08-25T17:00:00Z",
"assigned_to": 243
}'create-managed-task parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Task title |
description | string | No | Task description |
priority | string | No | low, medium, high, urgent (default: medium) |
due_date | string | No | Due date (ISO 8601) |
assigned_to | number | No | Primary assignee user ID (also added to work_item_assignees) |
assigned_users | number[] | No | Multiple assignee user IDs (first user becomes assigned_to) |
parent_id | string | No | UUID of a parent work item to link this task to (creates a child_of relationship) |
module_context | string | No | Module context (default: total-operations) |
tags | string[] | No | Array of tags |
custom_fields | object | No | Custom field key-value pairs |
notes | string | No | Additional notes |
Example — Add an existing work item to a user's managed task list:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "add-work-item-to-managed-task",
"work_item_id": "550e8400-e29b-41d4-a716-446655440000",
"target_user_id": 243,
"title": "Follow up on job",
"priority": "high",
"due_date": "2026-08-26T17:00:00Z"
}'This creates a task linked to the source work item via a child_of relationship in work_item_relationships, with reference metadata stored in custom_fields. If a managed task already exists for this work item + user, the existing task is returned with already_existed: true (idempotent).
add-work-item-to-managed-task parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
work_item_id | string | Yes | UUID of the source work item to add to managed tasks |
target_user_id | number | No | User ID to assign the managed task to (defaults to the API key owner) |
title | string | No | Override task title (defaults to source work item title) |
description | string | No | Override task description (defaults to source work item description) |
priority | string | No | low, medium, high, urgent (default: medium) |
due_date | string | No | Due date for the managed task (ISO 8601) |
reference_url | string | No | Optional deep link URL to the source item |
context_data | object | No | Additional metadata to store in custom_fields |
Example — Get an employee's due tasks:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "get-employee-due-tasks",
"user_id": 243,
"include_overdue": true,
"limit": 50
}'Returns tasks/subtasks assigned to the user (via assigned_to or work_item_assignees) that are due today or overdue, excluding terminal statuses. Results are sorted by due date (ascending) then priority (urgent first).
get-employee-due-tasks parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id | number | Yes | User ID to get due tasks for |
include_overdue | boolean | No | Include overdue tasks in results (default: true) |
limit | number | No | Results per page (max 500, default 200) |
offset | number | No | Pagination offset (default 0) |
Example — Get an employee's overdue tasks:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "get-employee-overdue-tasks",
"user_id": 243,
"limit": 50
}'Returns only overdue tasks (due_date < today) for the user, excluding terminal statuses. Same sorting as get-employee-due-tasks.
get-employee-overdue-tasks parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user_id | number | Yes | User ID to get overdue tasks for |
limit | number | No | Results per page (max 500, default 200) |
offset | number | No | Pagination offset (default 0) |
Typical AI agent workflow for managed tasks
A common pattern for an AI operations agent:
- Discover subordinates —
get-subordinateswithinclude_descendants: trueto get alluser_idsin the manager's reporting scope - Check due/overdue tasks —
get-employee-due-tasksandget-employee-overdue-tasksfor each user - Create new tasks —
create-managed-taskto assign work to a specific employee - Link existing work items —
add-work-item-to-managed-taskto add a job/invoice/appointment to someone's task list - Close completed tasks —
close-work-itemto mark tasks as completed
Example — Create an appointment:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "create-appointment",
"title": "Site inspection",
"scheduled_start": "2026-08-01T09:00:00Z",
"scheduled_end": "2026-08-01T11:00:00Z",
"contact_name": "John Smith",
"location_name": "Client Office"
}'Example — Add a comment:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "create-comment",
"work_item_id": "550e8400-e29b-41d4-a716-446655440000",
"comment_text": "Work completed and verified by client"
}'Example — Get a work item:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "get-work-item",
"work_item_id": "YOUR_WORK_ITEM_UUID",
"include": ["relationships", "attachments", "comments", "time_entries"]
}'Time & Attendance
| Action | Required Scope | Description |
|---|---|---|
get-attendance-logs | attendance:read | Get attendance clock in/out records |
get-shifts | shifts:read | Get shift templates and assignments |
get-roster | roster:read | Get shift roster entries |
get-locations | locations:read | Get locations and geofence identifiers |
get-timesheets | timesheets:read | Get timesheet records |
get-overtime | overtime:read | Get overtime records |
Example — Get attendance logs:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "get-attendance-logs",
"start_date": "2026-07-01",
"end_date": "2026-07-31",
"employee_id": 100,
"limit": 500,
"offset": 0
}'Finance
| Action | Required Scope | Description |
|---|---|---|
get-invoices | invoices:read | List invoices with filters |
get-invoice | invoices:read | Get a specific invoice by ID |
get-bills | bills:read | List bills with filters |
get-payments | payments:read | List payment records |
generate-finance-document-pdf | invoices:read or bills:read or quotes:read | Generate a PDF for any finance document (invoice, quote, credit note, purchase order, etc.) |
convert-finance-document | invoices:write or bills:write or quotes:write | Convert one finance document type to another (e.g. quote → invoice) |
generate-finance-document-pdf parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
work_item_id | string | Yes | UUID of the finance document work item |
doc_type | string | No | Document type key (e.g. invoice, quote, credit_note). Inferred from the work item if omitted |
format | string | No | Response format: raw (binary PDF, default) or base64 (JSON with base64-encoded PDF) |
Example — Generate a finance document PDF:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "generate-finance-document-pdf",
"work_item_id": "550e8400-e29b-41d4-a716-446655440000",
"doc_type": "invoice",
"format": "raw"
}'The response is a raw binary PDF when format=raw (default), or a JSON object with a base64 field when format=base64.
convert-finance-document parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
work_item_id | string | Yes | UUID of the source finance document |
source_doc_type | string | No | Source document type key (inferred from work item if omitted) |
target_doc_type | string | Yes | Target document type key (e.g. invoice, purchase_order) |
Example — Convert a quote to an invoice:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "convert-finance-document",
"work_item_id": "550e8400-e29b-41d4-a716-446655440000",
"source_doc_type": "quote",
"target_doc_type": "invoice"
}'Finance Tier Restrictions
Finance document types are gated by subscription tier
The finance document types available to a client are determined by their Total Finance subscription tier. Both the API and the UI enforce these restrictions:
- Server-side: The
generate-finance-document-pdfandconvert-finance-documentactions return403 Forbiddenif the requested document type is not allowed by the client's tier. - Client-side: The Public Data API Manager UI, Outbound Webhook Modal, and API Key Modal all filter finance document actions, events, and scopes based on the client's tier.
The following finance document types are subject to tier restrictions:
| Document Type Key | Typical Scope |
|---|---|
invoice | invoices:read / invoices:write |
quote | quotes:read / quotes:write |
credit_note | invoices:read / invoices:write |
purchase_order | bills:read / bills:write |
delivery_note | invoices:read |
proforma | invoices:read |
sales_order | quotes:read |
supplier_invoice | bills:read |
supplier_quote | bills:read |
expense | bills:read |
Contact your account manager to upgrade your Total Finance tier for access to additional document types.
Contacts
| Action | Required Scope | Description |
|---|---|---|
list-contacts | contacts:read | List customer and supplier contacts |
get-contact | contacts:read | Get a specific contact by ID |
search-contacts | contacts:read | Search contacts by name, email, phone |
list-contact-people | contacts:read | List contact people with filters |
search-contact-people | contacts:read | Quick search contact people by phone, email, or name |
list-contact-locations | contacts:read | List delivery/service locations for a contact |
list-contact-documents | contacts:read | List documents linked to a contact |
Threads & Messaging
| Action | Required Scope | Description |
|---|---|---|
list-threads | threads:read | List client threads with filters |
get-job-thread | threads:read | Get a job thread by job ID |
get-team-thread | threads:read | Get a shared team thread by team ID or solution key |
get-thread-messages | threads:read | Get messages from a thread |
send-thread-message | threads:send_message or threads:write | Send a message to a job or team thread |
Fleet
| Action | Required Scope | Description |
|---|---|---|
get-vehicles | vehicles:read | List vehicles with details |
get-vehicle-location | tracking:read | Get latest GPS location for a vehicle |
Reports & PDFs
| Action | Required Scope | Description |
|---|---|---|
generate-job-card-pdf | work_items:read | Generate a job card PDF for a work item |
generate-attendance-card-pdf | work_items:read | Generate an attendance card PDF for an appointment |
generate-finance-document-pdf | invoices:read or bills:read or quotes:read | Generate a PDF for a finance document (see Finance section for details) |
get-work-item-analytics | work_items:read | Get aggregated work item analytics |
get-work-item-sla | work_items:read | Get SLA status for work items |
Recurring Jobs
| Action | Required Scope | Description |
|---|---|---|
create-recurring-job | recurring:write or work_items:write | Create a recurring job schedule that auto-generates work items |
update-recurring-job | recurring:write or work_items:write | Update an existing recurring job schedule |
list-recurring-jobs | recurring:write or work_items:read | List recurring job schedules with optional filters |
get-recurring-job | recurring:write or work_items:read | Get a single recurring job schedule with generation history |
pause-recurring-job | recurring:write or work_items:write | Pause a recurring job schedule |
resume-recurring-job | recurring:write or work_items:write | Resume a paused recurring job schedule |
generate-recurring-job | recurring:write or work_items:write | Manually generate a job from a recurring schedule |
Example — Create a recurring job:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "create-recurring-job",
"name": "Weekly Site Inspection",
"description": "Auto-generated weekly site inspection job",
"contact_id": 42,
"priority": "high",
"assigned_to": 15,
"assigned_users": [15, 22],
"frequency": "weekly",
"day_of_week": 1,
"generate_time": "08:00:00",
"auto_assign": true,
"auto_approve": true,
"create_appointments": true,
"due_days_after_creation": 3,
"safety_requirements": "Hard hat and safety vest required",
"equipment_required": "Inspection camera, multimeter"
}'Example — Generate a job from a recurring schedule:
curl -X POST https://totalaccess.co.za/api/v1/public/data \
-H "X-API-Key: ta_sk_..." \
-H "Content-Type: application/json" \
-d '{
"action": "generate-recurring-job",
"id": "550e8400-e29b-41d4-a716-446655440000"
}'Response format
{
"success": true,
"data": [ ... ],
"meta": {
"action": "get-employees",
"count": 45,
"limit": 100,
"offset": 0,
"total": 45
}
}Error responses
{
"success": false,
"error": {
"code": "FORBIDDEN",
"message": "API key does not have required scope: employees:read"
}
}| Error Code | HTTP Status | Description |
|---|---|---|
UNAUTHORIZED | 401 | Missing, invalid, or expired API key |
FORBIDDEN | 403 | API key lacks required scope or IP not allowed |
TIER_RESTRICTED | 403 | Finance document type not allowed by client's subscription tier |
NOT_FOUND | 404 | Requested resource not found |
BAD_REQUEST | 400 | Invalid action or missing required parameters |
RATE_LIMITED | 429 | Rate limit exceeded |
Rate limiting
The Public Data API shares the same rate limits as the REST API. See Introduction for tier details.
Rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1700000000