API reference

Project

A job for one customer. Its invoices, sales receipts and credits belong to it, and bill and expense lines, time and expense claims can be put on it; its numbers come from those (all dates).

The project object

object string
Always "project".
id id
The project's id (a UUID). Use it to fetch it, or to refer to it from other things.
name string
Its name (each of a customer's projects has its own).
customer_id id
The customer it's for.
status string
not_started, in_progress, completed or canceled. New work goes only on projects not started or in progress.
start_date date or null
When it starts.
end_date date or null
When it ends.
notes string or null
Notes.
currency string
The business's currency: USD or CAD. All _cents amounts are in it.
income_cents integer
Income from its invoices, sales receipts and credits (in the books).
cost_cents integer
Costs in the books: its bill, expense, bank and journal lines, expense claims, and the cost of what it sold.
labour_cents integer
Employees' hours on it × their cost rate (not in the books until payroll).
profit_cents integer
income_cents − cost_cents − labour_cents.
hours number
Time worked on it, in hours.
unbilled_time_cents integer
Billable time on it that isn't on an invoice yet.
open_invoices_cents integer
What's still owed on its sent invoices.
created_at timestamp
When it was added, in UTC.
updated_at timestamp
When it last changed.

Example

{
  "object": "project",
  "id": "3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708108",
  "name": "Kitchen remodel",
  "customer_id": "3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708101",
  "status": "in_progress",
  "start_date": "2026-10-06",
  "end_date": null,
  "notes": null,
  "currency": "USD",
  "income_cents": 480000,
  "cost_cents": 126000,
  "labour_cents": 84000,
  "profit_cents": 270000,
  "hours": 40,
  "unbilled_time_cents": 36000,
  "open_invoices_cents": 120000,
  "created_at": "2026-10-06T14:30:00.000Z",
  "updated_at": "2026-10-06T14:30:00.000Z"
}

List projects

GET /api/v1/projects

Projects (a job for a customer), newest first, each with its income, costs, labour and profit so far.

Key: any (Read only works).

Query

limit integer
How many, 1 to 100 (default 25).
starting_after id
The last id of the page before, for the next page (when has_more is true).
filters
customer_id, status (not_started, in_progress, completed, canceled)

Newest first. Answers a list: { object: "list", url, has_more, data }.

Request

curl https://tallypip.com/api/v1/projects?limit=3 \
  -H "Authorization: Bearer tpk_your_key"

Answer: 200

{
  "object": "list",
  "url": "/api/v1/projects",
  "has_more": false,
  "data": [
    {
      "object": "project",
      "id": "3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708108",
      "name": "Kitchen remodel",
      "customer_id": "3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708101",
      "status": "in_progress",
      "start_date": "2026-10-06",
      "end_date": null,
      "notes": null,
      "currency": "USD",
      "income_cents": 480000,
      "cost_cents": 126000,
      "labour_cents": 84000,
      "profit_cents": 270000,
      "hours": 40,
      "unbilled_time_cents": 36000,
      "open_invoices_cents": 120000,
      "created_at": "2026-10-06T14:30:00.000Z",
      "updated_at": "2026-10-06T14:30:00.000Z"
    }
  ]
}

Try it

With your own key (Settings › Connected apps). It stays in this page only, and reading can't change anything.

Create a project

POST /api/v1/projects

Start a project for a customer.

Key: Read and change.

Body (JSON)

customer_id id
Required to start one. It can change only while nothing is on the project.
name string
Required to start one. Each of a customer's projects has its own name.
status string
not_started, in_progress (the default), completed or canceled. New work goes only on projects not started or in progress.
start_date, end_date date
Optional. On a change, null clears one.
notes string
Optional.

Answers 201 with the new project. Refused with 400 and the reason (nothing is saved); send an Idempotency-Key so a retry can't make it twice.

Request

curl https://tallypip.com/api/v1/projects \
  -H "Authorization: Bearer tpk_your_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: <a new id for each change>" \
  -d '{
    "customer_id": "3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708101",
    "name": "Kitchen remodel",
    "start_date": "2026-10-06"
  }'

Answer: 201

{
  "object": "project",
  "id": "3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708108",
  "name": "Kitchen remodel",
  "customer_id": "3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708101",
  "status": "in_progress",
  "start_date": "2026-10-06",
  "end_date": null,
  "notes": null,
  "currency": "USD",
  "income_cents": 480000,
  "cost_cents": 126000,
  "labour_cents": 84000,
  "profit_cents": 270000,
  "hours": 40,
  "unbilled_time_cents": 36000,
  "open_invoices_cents": 120000,
  "created_at": "2026-10-06T14:30:00.000Z",
  "updated_at": "2026-10-06T14:30:00.000Z"
}

Retrieve a project

GET /api/v1/projects/{id}

One project with its numbers.

Key: any (Read only works).

Request

curl https://tallypip.com/api/v1/projects/3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708108 \
  -H "Authorization: Bearer tpk_your_key"

Answer: 200

{
  "object": "project",
  "id": "3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708108",
  "name": "Kitchen remodel",
  "customer_id": "3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708101",
  "status": "in_progress",
  "start_date": "2026-10-06",
  "end_date": null,
  "notes": null,
  "currency": "USD",
  "income_cents": 480000,
  "cost_cents": 126000,
  "labour_cents": 84000,
  "profit_cents": 270000,
  "hours": 40,
  "unbilled_time_cents": 36000,
  "open_invoices_cents": 120000,
  "created_at": "2026-10-06T14:30:00.000Z",
  "updated_at": "2026-10-06T14:30:00.000Z"
}

Try it

With your own key (Settings › Connected apps). It stays in this page only, and reading can't change anything.

Update a project

PATCH /api/v1/projects/{id}

Change a project: send only what changes (its status, to complete or cancel it; its dates; its name).

Key: Read and change.

Body (JSON)

customer_id id
Required to start one. It can change only while nothing is on the project.
name string
Required to start one. Each of a customer's projects has its own name.
status string
not_started, in_progress (the default), completed or canceled. New work goes only on projects not started or in progress.
start_date, end_date date
Optional. On a change, null clears one.
notes string
Optional.

Send only what changes. Answers 200 with the project as it is now. Refused with 400 and the reason (nothing is changed).

Request

curl -X PATCH https://tallypip.com/api/v1/projects/3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708108 \
  -H "Authorization: Bearer tpk_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "status": "completed",
    "end_date": "2026-12-18"
  }'

Answer: 200

{
  "object": "project",
  "id": "3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708108",
  "name": "Kitchen remodel",
  "customer_id": "3f6c1e2a-8b4d-4c1e-9a7b-2d5e6f708101",
  "status": "in_progress",
  "start_date": "2026-10-06",
  "end_date": null,
  "notes": null,
  "currency": "USD",
  "income_cents": 480000,
  "cost_cents": 126000,
  "labour_cents": 84000,
  "profit_cents": 270000,
  "hours": 40,
  "unbilled_time_cents": 36000,
  "open_invoices_cents": 120000,
  "created_at": "2026-10-06T14:30:00.000Z",
  "updated_at": "2026-10-06T14:30:00.000Z"
}