The Tidyflow API is a RESTful JSON API that allows you to programmatically manage clients, jobs, contacts, time logs, and users within your Tidyflow account.
Base URL: https://app.tidyflow.com/api/v1
Authentication
All API requests require a Personal Access Token sent via the Authorization header. Tokens are scoped to the user who created them — all actions are performed as that user, respecting their role and permissions.
To create a token:
- Log in to your Tidyflow account.
- Go to Settings → API Tokens.
- Click New Token and copy the token immediately — it won’t be shown again.
Required Headers
| Header | Value | When |
|---|---|---|
Authorization | Bearer YOUR_API_TOKEN | Always |
Accept | application/json | Always |
Content-Type | application/json | POST and PUT requests |
Verify Your Token
GET /api/v1/me HTTP/1.1
Host: app.tidyflow.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json
Returns the authenticated user’s profile:
{
"id": 25,
"firstname": "Sarah",
"lastname": "Johnson",
"email": "[email protected]",
"fullname": "Sarah Johnson",
"timezone": "America/New_York",
"active": true
}
Clients
List Clients
GET /api/v1/clients HTTP/1.1
Host: app.tidyflow.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
search | string | Filter by name |
tags | array | Filter by tag IDs |
country_id | integer | Filter by country |
limit | integer | Results per page (default: 1000) |
page | integer | Page number |
Response:
{
"data": [
{
"uuid": "7c4d3e3c-7fc5-4895-925c-d3b6f70eec02",
"name": "Acme Corp",
"client_id": null,
"website": "https://acme.com",
"reg_number": "12345678",
"country": "United States",
"state": "TX",
"city": "Austin",
"street_address": "123 Main St",
"postal_code": "73301",
"is_archived": false,
"contacts": []
}
],
"links": { "first": "...", "last": "...", "prev": null, "next": "..." },
"meta": { "current_page": 1, "last_page": 1, "per_page": 1000, "total": 12 }
}
Custom field values are included as additional top-level keys on each client object when custom fields are configured.
Get Client
GET /api/v1/clients/{uuid} HTTP/1.1
Returns a single client by UUID.
Search Clients
GET /api/v1/clients/search?q={query} HTTP/1.1
Returns a lightweight list of matching clients (id, uuid, name).
Create Client
POST /api/v1/clients HTTP/1.1
Host: app.tidyflow.com
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: application/json
{
"name": "Acme Corp",
"website": "https://acme.com",
"contact": {
"firstname": "John",
"lastname": "Doe",
"email": "[email protected]"
}
}
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Client name (max 100) |
client_id | string | No | Your internal client ID (max 100, unique) |
reg_number | string | No | Registration number (max 100, unique) |
website | string | No | Website URL |
country_id | integer | No | Country ID |
state | string | No | State/province |
city | string | No | City |
street_address | string | No | Street address |
postal_code | string | No | Postal/ZIP code |
client_groups | array | No | Array of client group IDs |
contact | object | No | Create and link a contact (see below) |
Nested contact object (all fields required when contact is provided):
| Field | Type | Description |
|---|---|---|
contact.firstname | string | First name |
contact.lastname | string | Last name |
contact.email | string | Email address (must be unique) |
contact.title | string | Job title (optional) |
contact.phone_number | string | Phone number (optional) |
Update Client
PUT /api/v1/clients/{uuid} HTTP/1.1
Supports partial updates — only include the fields you want to change. Accepts the same fields as Create, plus:
| Field | Type | Description |
|---|---|---|
tags | array | Array of tag IDs to assign |
Jobs
List Jobs
GET /api/v1/jobs HTTP/1.1
Host: app.tidyflow.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
search | string | Search by job name |
client_uuid | string | Filter by client UUID |
users | array | Filter by assigned user IDs |
statuses | array | Filter by status slugs |
tags | array | Filter by tag IDs |
start_date_range | string | Filter by start date range |
due_date_range | string | Filter by due date range |
include_completed | boolean | Include completed jobs (default: false) |
sort_by | string | Sort field |
sort_direction | string | asc or desc |
page | integer | Page number |
Response:
{
"data": [
{
"uuid": "8cbfec20-ec1e-4539-8352-aa49196c95c2",
"name": "Tax Return",
"status": null,
"client_uuid": "7c4d3e3c-...",
"estimated_hours": 5.0,
"start_date": "2026-03-01",
"due_date": "2026-03-31",
"is_overdue": false,
"is_completed": false,
"is_repeating": false
}
],
"links": { ... },
"meta": { ... }
}
Get Job
GET /api/v1/jobs/{uuid} HTTP/1.1
Returns a single job with related data (client, users, tags, subtasks).
Create Job
POST /api/v1/jobs HTTP/1.1
Host: app.tidyflow.com
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: application/json
{
"name": "Monthly Bookkeeping",
"client_uuid": "7c4d3e3c-7fc5-4895-925c-d3b6f70eec02",
"due_date": "2026-04-15",
"estimated_hours": 3
}
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Job name (max 150) |
description | string | No | Job description |
client_uuid | string | No | Client UUID to link to |
user_id | integer | No | Assigned user ID |
status | string | No | Status slug |
tags | array | No | Array of tag IDs |
start_date | date | No | Start date (YYYY-MM-DD) |
due_date | date | No | Due date (YYYY-MM-DD) |
estimated_hours | number | No | Estimated hours (0–1000) |
Contacts
List Contacts
GET /api/v1/contacts HTTP/1.1
Host: app.tidyflow.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
search | string | Filter by name |
limit | integer | Results per page (default: 1000) |
page | integer | Page number |
Response:
{
"data": [
{
"uuid": "7d3c7935-ac14-45d7-a75e-eb77ac5eb741",
"firstname": "John",
"lastname": "Doe",
"preferred_name": "John",
"email": "[email protected]",
"phone_number": "+1 (555) 123-4567",
"portal_enabled": true
}
],
"links": { ... },
"meta": { ... }
}
Get Contact
GET /api/v1/contacts/{uuid} HTTP/1.1
Returns a single contact by UUID.
Create Contact
POST /api/v1/contacts HTTP/1.1
Host: app.tidyflow.com
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: application/json
{
"firstname": "John",
"lastname": "Doe",
"email": "[email protected]"
}
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
firstname | string | Yes | First name |
lastname | string | Yes | Last name |
email | string | Yes | Email address (must be unique) |
preferred_name | string | No | Preferred/display name |
phone_number | string | No | Phone number |
portal_enabled | boolean | No | Enable contact portal access (default: true) |
Update Contact
PUT /api/v1/contacts/{uuid} HTTP/1.1
Supports partial updates — only include the fields you want to change. Accepts the same fields as Create.
Users
List Users
GET /api/v1/users HTTP/1.1
Host: app.tidyflow.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
search | string | Filter by name or email |
Response:
{
"data": [
{
"id": 245,
"firstname": "Emily",
"lastname": "Chen",
"fullname": "Emily Chen",
"email": "[email protected]",
"initials": "EC",
"timezone": "America/Chicago",
"role_name": "Standard"
}
]
}
Get User
GET /api/v1/users/{id} HTTP/1.1
Find User by Email
GET /api/v1/users/email/{email} HTTP/1.1
Returns the user matching the given email, or {"data": null} if not found.
Time Logs
List Time Logs
GET /api/v1/time-logs HTTP/1.1
Host: app.tidyflow.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
range | string | Date range filter |
client_uuid | string | Filter by client UUID |
tasks | array | Filter by task IDs |
users | array | Filter by user IDs |
search | string | Search by note |
per_page | integer | Results per page |
page | integer | Page number |
Response:
{
"data": [
{
"uuid": "e3f9da02-d916-43eb-a9e3-2687c7031934",
"client": { "uuid": "8c1318a9-...", "name": "Acme Corp" },
"task": { "uuid": "af5e5172-...", "name": "Tax Return" },
"user": { "id": 25, "fullname": "Sarah Johnson" },
"note": "Completed reconciliation",
"hours": "8.00",
"date": "2026-01-08"
}
],
"links": { ... },
"meta": { ... }
}
Pagination
All list endpoints return paginated results with links and meta objects:
{
"data": [ ... ],
"links": {
"first": "https://app.tidyflow.com/api/v1/clients?page=1",
"last": "https://app.tidyflow.com/api/v1/clients?page=5",
"prev": null,
"next": "https://app.tidyflow.com/api/v1/clients?page=2"
},
"meta": {
"current_page": 1,
"last_page": 5,
"per_page": 15,
"total": 72
}
}
Error Responses
| Status | Meaning | Description |
|---|---|---|
401 | Unauthenticated | Invalid or missing API token |
403 | Forbidden | Valid token but insufficient permissions |
404 | Not Found | Resource not found or doesn’t belong to your account |
422 | Validation Error | Invalid request data |
Validation errors include field-level detail:
{
"message": "The name field is required.",
"errors": {
"name": ["The name field is required."]
}
}
Rate Limiting
Rate limits are set at 500 requests per hour per user. Exceeding this limit results in a 429 Too Many Requests status code.