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 |
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
}'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.) |
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 |
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 — 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