Connection details
Use the versioned API base for requests, and use the resource value when requesting OAuth API scopes. Send API tokens and OAuth access tokens as bearer tokens.
https://doubletime.io/api/v1https://doubletime.io/apiAuthorization: Bearer <token>/api defaults to the current version, which is v1. Use /api/v1 when you want the version to be explicit.
Authentication options
Choose the narrowest credential type that fits the integration.
| Method | Best for | Notes |
|---|---|---|
| Fine-grained API tokens | Best for private scripts and internal automations. Tokens use the API resource and only work for the scopes selected when the token is created. |
|
| OAuth access tokens | Best for apps used by other people. Register an OAuth client, send the user through consent, then call the API with the returned access token. |
|
| Legacy tokens | Older dt_ tokens have broad access to the owner account. They still work, but new integrations should use fine-grained tokens or OAuth. |
|
Examples
Read profile
curl -H "Authorization: Bearer dtfg_your_token" \
https://doubletime.io/api/v1/profileCreate task
curl -X POST https://doubletime.io/api/v1/tasks \
-H "Authorization: Bearer dtfg_your_token" \
-H "Content-Type: application/json" \
-d '{"summary":"Follow up with client","tags":[]}'Scopes
Request the smallest set of scopes that match the action. Write and send scopes depend on the matching read scope.
| Area | Scopes | Use |
|---|---|---|
| Profile | profile:read | Read the signed-in user profile. |
| Billing Hub | billing:read | Read Billing Hub summary and invoice-selection data. |
| Tasks and time | tasks:readtasks:writetime:readtime:write | Read and update tasks, task notes, ordering, and task time entries. |
| Clients and projects | clients:readclients:writeprojects:readprojects:write | Read, create, update, and archive client and project records. |
| Tags and rules | tags:readtags:writerules:readrules:write | Manage tags plus automation and billing rules. Inline new tags require tags:write. |
| Invoices | invoices:readinvoices:writeinvoices:send | Manage invoices, adjustments, payments, attachments, previews, sends, and receipt dispatches. |
| Expenses | expenses:readexpenses:write | Read and manage expense records, expense lines, and expense attachments. |
| Reports | reports:read | Run task, time, income, tax, receivables, billing, and client-health reports. |
| Settings | settings:readsettings:write | Read and update non-sensitive account settings through the API resource only. |
Payloads are scope-aware. If a token can read the requested record but not a related record, DoubleTime omits nested details for that related resource. Request the related read scope when your integration needs linked client, project, task, invoice, or tag detail.
Endpoints
This is the public API surface intended for scoped tokens and OAuth clients. Reads need the read scope for the area, and mutations need the matching write scope.
Profile
profile:read- GET
/api/v1/profile
Billing Hub
billing:read- GET
/api/v1/billing-hub/summary - GET
/api/v1/billing-hub/invoice-selection
Tasks
tasks:readtasks:write- GETPOST
/api/v1/tasks - GET
/api/v1/tasks/notes - GET
/api/v1/tasks/invoice-import-candidates - PATCH
/api/v1/tasks/reorder - GETPUTDELETE
/api/v1/tasks/:task
Time entries
time:readtime:write- GETPOST
/api/v1/tasks/:task/activities - GETPUTDELETE
/api/v1/tasks/:task/activities/:activity
Clients and projects
clients:readclients:writeprojects:readprojects:write- GETPOST
/api/v1/clients - GETPUTDELETE
/api/v1/clients/:client - GETPOST
/api/v1/clients/:client/projects - GETPUTDELETE
/api/v1/clients/:client/projects/:project
Tags and rules
tags:readtags:writerules:readrules:write- GETPOST
/api/v1/tags - GETPUTDELETE
/api/v1/tags/:tag - GETPOST
/api/v1/rules - GETPUTDELETE
/api/v1/rules/:rule - PATCH
/api/v1/rules/:rule/enabled
Invoices
invoices:readinvoices:writeinvoices:send- GETPOST
/api/v1/invoices - GETPUTDELETE
/api/v1/invoices/:invoice - GET
/api/v1/invoices/:invoice/import - GETPOST
/api/v1/invoices/:invoice/adjustments - DELETE
/api/v1/invoices/:invoice/adjustments/:adjustment - GETPOST
/api/v1/invoices/:invoice/payments - DELETE
/api/v1/invoices/:invoice/payments/:invoicePayment - POST
/api/v1/invoices/:invoice/payments/:invoicePayment/send-receipt - GET
/api/v1/invoices/:invoice/artifacts - DELETE
/api/v1/invoices/:invoice/artifacts/:invoiceArtifact - GET
/api/v1/invoices/:invoice/artifacts/:invoiceArtifact/download - GET
/api/v1/invoices/:invoice/dispatches - POST
/api/v1/invoices/:invoice/dispatches/:invoiceDispatch/resend - POST
/api/v1/invoices/:invoice/preview - POST
/api/v1/invoices/:invoice/send
Invoice deposit candidates
invoices:read- GET
/api/v1/invoices/:invoice/deposit-candidates
Invoice expense candidates
invoices:readexpenses:read- GET
/api/v1/invoices/:invoice/expense-candidates
Link selected expense attachments to an invoice
invoices:readinvoices:writeexpenses:read- POST
/api/v1/invoices/:invoice/artifacts/from-expense
Expenses
expenses:readexpenses:write- GETPOST
/api/v1/expenses - GETPUTDELETE
/api/v1/expenses/:expense - GET
/api/v1/expenses/:expense/artifacts - DELETE
/api/v1/expenses/:expense/artifacts/:expenseArtifact - GET
/api/v1/expenses/:expense/artifacts/:expenseArtifact/download
Reports
reports:read- GET
/api/v1/reports/summary - GET
/api/v1/reports/task-time - GET
/api/v1/reports/daily-time - GET
/api/v1/reports/income - GET
/api/v1/reports/tax - GET
/api/v1/reports/receivables - GET
/api/v1/reports/unbilled-wip - GET
/api/v1/reports/collections - GET
/api/v1/reports/client-health - GET
/api/v1/reports/billing-pipeline - GET
/api/v1/reports/billing-coverage - GET
/api/v1/reports/unpaid-invoices - GET
/api/v1/reports/billed-vs-collected - GET
/api/v1/reports/tax-liability - GET
/api/v1/reports/timesheet - GET
/api/v1/reports/project-time-breakdown - GET
/api/v1/reports/tag-time-breakdown - GET
/api/v1/reports/expenses - GET
/api/v1/reports/profit-loss
Settings
settings:readsettings:write- GET
/api/v1/settings - PATCH
/api/v1/settings/business - PATCH
/api/v1/settings/invoice - PATCH
/api/v1/settings/locale - PATCH
/api/v1/settings/notifications
Deposit invoices
Deposits use the existing invoice endpoints. Creating or finalising a deposit invoice requires an active Pro subscription, invoices:read and invoices:write. Reading deposits and applying an existing paid deposit remain available on every plan with the required scopes.
Request an upfront payment
POST /api/v1/invoices with type set to deposit creates a draft deposit invoice. Omit type, or use standard, for an ordinary invoice. The type cannot change after creation. Replace the example client and tax rate IDs with records available to the account; taxRateId can be null for an untaxed deposit. A null invoiceNumber selects the next invoice number.
{
"type": "deposit",
"clientId": 77,
"invoiceNumber": null,
"invoiceDate": "2026-09-28",
"dueDate": "2026-10-28",
"finalize": false,
"items": [
{
"description": "Website project deposit",
"quantity": 1,
"quantityUnit": "unit",
"unitPrice": 300,
"taxRateId": 3
}
]
}JSON body for POST /api/v1/invoices. This example requests 300 before tax.
Finalise the draft through PUT /api/v1/invoices/:invoice with finalize set to true, preserving its current invoice fields and items. Send it through the ordinary invoice send endpoint, which requires invoices:read and invoices:send. Record money actually received through POST /api/v1/invoices/:invoice/payments with paidTotal and, optionally, paidAt; paidTotal includes tax. Payment receipts use the existing send-receipt endpoint.
Find a deposit for a work invoice
GET /api/v1/invoices?filter[type]=deposit lists deposit invoices. Invoice responses include type and a deposit summary with amount, usedAmount, availableAmount and draws. These amounts are before tax and include reservations made by drafts.
Once the later work invoice exists, GET /api/v1/invoices/:invoice/deposit-candidates returns an array of its contact's finalised deposits that have money left or are already used on that invoice. Each includes id, descriptions, taxRateId, availableAmount, taxTotal, availableTaxTotal, isPaidInFull and unavailableReason. There is no pagination.
Only use a candidate whose unavailableReason is null. A deposit must be paid in full and match the invoice's contact and currency. The reasons unpaid and currency explain an unavailable candidate. availableAmount excludes amounts reserved by other invoices, but includes this invoice's existing draw so it can be replaced when editing.
Apply or release a deposit
Read the work invoice first, then PUT /api/v1/invoices/:invoice with its current required fields and the complete desired items array. Preserve existing item IDs and link fields; omitted items are removed. Add a line with depositInvoiceId set to the candidate id, quantity 1, quantityUnit unit, the candidate taxRateId and a negative unitPrice equal to the amount before tax. The following example assumes work item 789 and paid deposit 123, both using tax rate 3. Replace the fields with values from your invoice and candidate response.
{
"clientId": 77,
"invoiceNumber": 337,
"invoiceDate": "2026-09-28",
"dueDate": "2026-10-28",
"items": [
{
"id": 789,
"description": "Website work",
"quantity": 1,
"quantityUnit": "unit",
"unitPrice": 1000,
"taxRateId": 3
},
{
"depositInvoiceId": 123,
"description": "Deposit paid, invoice 336",
"quantity": 1,
"quantityUnit": "unit",
"unitPrice": -300,
"taxRateId": 3
}
]
}JSON body for updating a work invoice containing only the existing item 789.
A deposit draw cannot exceed availableAmount or make the work invoice subtotal or total negative. Use at most two decimal places for the amount. The server calculates the tax credit from the original deposit, including rounding, and returns the saved totals. Do not record another payment for the applied amount: the negative item reduces the amount due.
One deposit can cover several invoices. Draft items reserve their amounts; removing a deposit item releases its amount. Resend depositInvoiceId when retaining a linked item. Read candidates again if a concurrent update changes the available balance.
Deposit invoice items must share one tax rate and cannot link tracked time, expenses or other deposits. Deposit invoices cannot have adjustments. Once another invoice draws on a deposit, its contact, tax and item amounts are locked. Refunds of unused deposits are not supported.
Implementation notes
Requests and pagination
- JSON endpoints expect Content-Type: application/json unless the endpoint is a file upload or download.
- Task lists support limit, offset, and sort query parameters and return X-Total-Count, X-Offset, and X-Limit.
Scopes and subscriptions
- Read scopes allow viewing records. Write scopes require the matching read scope and can create, update, archive, or delete records.
- Preview, send, receipt, and resend invoice endpoints require invoices:send plus invoices:read.
- Some report endpoints require an active subscription in addition to reports:read.
Invoices and expenses
- For an existing invoice, GET /api/v1/invoices/:invoice/import is the preferred import-candidate query because the invoice supplies the client and existing linked activity exclusions.
- Reading expense records, removing attachment links, and deleting draft expenses remain available on every plan with the required scopes. Existing downloads depend on Google authorisation and file availability, not an active subscription. Creating or updating expense financial records requires an active subscription.
- Listing invoice expense candidates and linking selected expense attachments to an invoice require a subscription and the scopes listed above.
- Adding, removing, or reassigning expense links requires expenses:write and expenses:read alongside invoices:write and invoices:read, plus an active subscription. Invoice updates that preserve existing expense links keep the base invoice access requirements.
- The expenses and profit-loss report endpoints require reports:read and an active subscription. They use finalised documents by expense or invoice date, regardless of payment timing. Profit and loss values exclude tax; mixed currencies are not automatically converted.
Attachments
- Add new Google Drive files through Google Picker in the DoubleTime app for first-time file selection and authorisation. DoubleTime uses drive.file for per-file access; another Drive connector's access does not grant DoubleTime access. Use REST to manage attachments already linked in the app.
- The from-expense endpoint requires expenseId and an explicit, nonempty expenseArtifactIds array. List the expense attachments first and pass only IDs selected by the user; omission never means all attachments.
- Sharing a selected receipt creates an independent invoice attachment, not a file copy. Removing a document attachment link does not delete its Google file or the link on another document.
- Receipt sharing requires an active subscription and creates links atomically. A failed batch creates no links, and repeating a selection returns existing links without duplicates. Confirm the selected receipts and invoice before sharing: invoice recipients can download them. If sharing fails after the invoice was saved, retry against that invoice ID rather than creating another invoice.
- Direct invoice and expense attachment uploads are retired. POST to either document's /artifacts collection returns HTTP 410 with ARTIFACT_UPLOAD_RETIRED; add files through Picker in the app.