Tidyflow Help

AI ASSISTANT

Loading your conversation

Hi there.

How can we help?

Ask anything about Tidyflow. We’ll help you find your next step.

Try asking

Ask a question…

Checking your sign-in…

Clear this history?

This removes these conversations from this browser. It doesn’t retract requests or emails already sent to the team.

Hi there.

How can we help?

Ask anything about Tidyflow. We’ll help you find your next step.

Try asking

Looking through Tidyflow Help…

You’re in good hands.

Request received.

We couldn’t confirm receipt. . Retrying the same details won’t create a second request.

Send this to our team

Your conversation is included. We’ll reply by email.

Up to 3 images · 5 MB each, 6 MB total. Only the support team sees these.

By sending, you agree to share this conversation and any screenshots with Tidyflow support. Privacy policy

Tidyflow support · Here to help

API

API Reference

Build custom integrations against your Tidyflow tenant — sync clients, create jobs, post time logs, and pull reporting data.


Base URL & versioning

https://app.tidyflow.com/api/v1

There is currently one version (v1). Breaking changes will be introduced under a new prefix.


Authentication

The Tidyflow API uses personal access tokens sent as a Bearer header.

Generate a token

  1. Log in to Tidyflow.
  2. Open Settings > API tokens.
  3. Click New token, give it a name (e.g. Zapier, Internal sync), and copy the token immediately — it will not be shown again.
  4. Revoke a token at any time by deleting it from the same page.

Each token inherits the permissions of the user that created it and is scoped to that user’s tenant. Tokens do not expire; revoke any token that may have been exposed.

Sending the token

Add the token to every request:

Authorization: Bearer <your-token>
Accept: application/json

A missing or invalid token returns 401 Unauthenticated.


Conventions

Identifiers

Every resource exposes a stable uuid — always use the UUID in URLs and references, not the numeric id.

Pagination

List endpoints return a standard Laravel paginator:

{
  "data": [ ... ],
  "links": { "first": "...", "last": "...", "prev": null, "next": "..." },
  "meta": { "current_page": 1, "last_page": 10, "per_page": 50, "total": 480 }
}

Control page size with ?limit= (clients, contacts) or ?per_page= (time logs). Default page size is large — set an explicit limit for predictable results.

Filtering

Filters are passed as query parameters. Array filters use repeated keys: ?tags[]=12&tags[]=15.

Errors

StatusMeaning
401Missing or invalid token
403Token does not have access to this record (wrong tenant or insufficient permission)
404Resource not found
422Validation error — errors object lists each field
429Rate limit exceeded — slow down and retry

Validation error body:

{
  "message": "The given data was invalid.",
  "errors": {
    "name": ["The name field is required."]
  }
}

Rate limits

Each API token can make up to 300 requests per minute across the API, and some endpoints have lower limits. Over a limit, the API returns 429 Too Many Requests with a Retry-After header giving the seconds to wait. Login is rate-limited separately.


Users

Read-only. Useful for resolving user IDs when assigning clients or jobs.

List users

GET /api/v1/users

Query parameters: search.

Get user by ID

GET /api/v1/users/{id}

Find user by email

GET /api/v1/users/email/{email}

Returns the user object, or { "data": null } if no user matches.

User shape

{
  "id": 42,
  "fullname": "Jane Doe",
  "firstname": "Jane",
  "lastname": "Doe",
  "email": "jane@example.com",
  "initials": "JD",
  "avatar_url": "https://...",
  "role": "Admin",
  "timezone": "Australia/Sydney",
  "client_access": "all",
  "is_contractor": false,
  "is_owner": false,
  "archived_date": null
}

client_access is one of all, client_groups, or assigned_clients, and reflects how a Standard user’s client visibility is limited. is_contractor is deprecated (it is true only when client_access is assigned_clients); use client_access instead.


Clients

List clients

GET /api/v1/clients

Query parameters:

NameTypeDescription
searchstringMatch on name, registration number, etc.
contact_emailstringClients linked to a contact with this exact email (case-insensitive)
client_statusstringprospect or active. Leave out for both
country_idintCountry filter
sort_bystringname (default) or created_at
sort_directionstringasc (default) or desc
limitintPage size

Search clients (autocomplete)

GET /api/v1/clients/search?q=acme

Returns up to 10 lightweight matches:

{ "clients": [ { "id": 7, "uuid": "...", "name": "Acme Pty Ltd" } ] }

Get a client

GET /api/v1/clients/{uuid}

Create a client

POST /api/v1/clients

Body:

FieldRequiredNotes
name✓Max 100 chars
client_statusprospect or active (default). Prospects have not engaged the firm yet; accepting a proposal makes them active
client_idYour internal ref (must be unique within your workspace)
reg_numberBusiness registration number (must be unique within your workspace)
website
country_idID from the countries reference
user_idAccount manager (Tidyflow user ID)
state, city, street_address, postal_code
client_groupsArray of client group IDs
contactOptional nested object — creates and links a primary contact in one call

Nested contact object:

FieldRequiredNotes
firstname✓
lastname✓
email✓Must be unique across all contacts, ignoring case
titlee.g. Director
phone_number
include_for_client_requestsboolean

Returns the created client in the same shape as GET /clients/{uuid}.

Update a client

PUT /api/v1/clients/{uuid}

All fields are optional — send only what you want to change. Accepts the same fields as create (excluding the nested contact), plus tags (array of tag IDs).

Client shape

{
  "uuid": "...",
  "name": "Acme Pty Ltd",
  "client_id": "AC-001",
  "reg_number": "12 345 678 901",
  "website": "https://acme.com",
  "country": "Australia",
  "state": "NSW",
  "city": "Sydney",
  "street_address": "1 George St",
  "postal_code": "2000",
  "year_end": "06-30",
  "year_end_text": "30 June",
  "is_archived": false,
  "client_status": "active",
  "created_at": "2026-09-12T02:30:32+00:00",
  "contacts": [ ... ]
}

Custom fields defined in your tenant are merged into the response keyed by field name.


Contacts

List contacts

GET /api/v1/contacts

Query parameters:

NameTypeDescription
emailstringExact email match (case-insensitive)
limitintPage size

Get a contact

GET /api/v1/contacts/{uuid}

Create a contact

POST /api/v1/contacts
FieldRequiredNotes
firstname✓
lastname✓
email✓Must be unique across all contacts, ignoring case
preferred_nameDefaults to firstname
phone_number
portal_enabledboolean — defaults to true (contact gets a client portal login)
client_uuidLinks the new contact to this client
titleThe contact’s role at the client, e.g. Director. Used with client_uuid

Update a contact

PUT /api/v1/contacts/{uuid}

All fields optional.

Contact shape

{
  "uuid": "...",
  "firstname": "Jane",
  "lastname": "Doe",
  "preferred_name": "Jane",
  "email": "jane@acme.com",
  "phone_number": "+61 400 000 000",
  "portal_enabled": true,
  "clients": [ { "uuid": "...", "name": "Acme Pty Ltd" } ]
}

Jobs

The Tidyflow UI calls these “Jobs”. The API path uses /jobs.

List jobs

GET /api/v1/jobs

Query parameters:

NameTypeDescription
searchstringMatch on job name
client_uuidstringFilter to one client
users[]int[]Assigned user IDs
tags[]int[]Tag IDs
statuses[]string[]Status slugs
start_date_rangestringDate range filter
due_date_rangestringDate range filter
include_completedboolDefault false
exclude_subtasksbool
exclude_client_requestsbool
sort_by, sort_directionstring

Get a job

GET /api/v1/jobs/{uuid}

Includes client, assignees, followers, tags, parent, and subtasks.

Create a job

POST /api/v1/jobs
FieldRequiredNotes
name✓Max 150 chars
descriptionUp to 30,000 chars
client_uuidLink the job to a client
user_idAssignee
statusA status slug from your tenant
tagsArray of tag IDs
start_dateISO date, must be ≤ due_date
due_dateISO date
internal_due_date, extended_due_dateISO dates
estimated_hoursNumeric, 0–1000

Job shape (key fields)

{
  "uuid": "...",
  "name": "FY24 financials",
  "description": "...",
  "status": "in_progress",
  "status_name": "In progress",
  "client_uuid": "...",
  "client": { ... },
  "users": [ ... ],
  "tags": [ ... ],
  "start_date": "2026-05-01",
  "due_date": "2026-06-30",
  "estimated_hours": 12,
  "is_overdue": false,
  "is_completed": false,
  "completed_date": null,
  "is_repeating": false,
  "is_subtask": false,
  "parent_uuid": null,
  "sub_tasks": [ ... ]
}

Repeating-job fields are also returned — see the full payload in a real response if you need to replicate recurrence.


Time logs

Read-only listing of recorded time.

List time logs

GET /api/v1/time-logs

Query parameters:

NameTypeDescription
rangestringDate range
client_uuidstringFilter by client
tasks[]string[]Job UUIDs
users[]int[]User IDs
searchstringMatch on note
per_pageintPage size

Time log shape

{
  "uuid": "...",
  "client": { "uuid": "...", "name": "Acme Pty Ltd" },
  "task":   { "uuid": "...", "name": "FY24 financials" },
  "user":   { "id": 42, "fullname": "Jane Doe" },
  "note": "Reviewed trial balance",
  "hours": 1.5,
  "hours_time": "01:30",
  "date": "2026-05-01",
  "formatted_date": "1 May 2026",
  "created_at": "2026-05-01T09:14:22Z",
  "updated_at": "2026-05-01T09:14:22Z"
}

Was this page helpful?

What went wrong?

Thanks for your feedback.

Last updated September 26, 2026