DoubleTime LogoDoubleTime Logo
Docs
/
REST API

Developers

REST API

Use DoubleTime's API for private automations, reporting scripts, and custom apps that need controlled access to account data.

REST API
MCP server
OAuth apps
chevron_leftchevron_right

On this page

  • Connection details
  • Authentication
  • Examples
  • Scopes
  • Endpoints
  • Deposit invoices
  • Implementation notes

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.

API base
https://doubletime.io/api/v1
OAuth API resource
https://doubletime.io/api
Authorization header
Authorization: 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.

MethodBest forNotes
Fine-grained API tokensBest for private scripts and internal automations. Tokens use the API resource and only work for the scopes selected when the token is created.
  • Token prefix: dtfg_
  • Use with Authorization: Bearer <token>
  • Can be revoked from DoubleTime
OAuth access tokensBest for apps used by other people. Register an OAuth client, send the user through consent, then call the API with the returned access token.
  • Authorization code with PKCE is supported
  • Use the API resource when requesting API scopes
Legacy tokensOlder dt_ tokens have broad access to the owner account. They still work, but new integrations should use fine-grained tokens or OAuth.
  • Token prefix: dt_
  • Avoid for new integrations when scoped access is available

Examples

Read profile

curl -H "Authorization: Bearer dtfg_your_token" \ https://doubletime.io/api/v1/profile

Create 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.

AreaScopesUse
Profileprofile:readRead the signed-in user profile.
Billing Hubbilling:readRead Billing Hub summary and invoice-selection data.
Tasks and timetasks:readtasks:writetime:readtime:writeRead and update tasks, task notes, ordering, and task time entries.
Clients and projectsclients:readclients:writeprojects:readprojects:writeRead, create, update, and archive client and project records.
Tags and rulestags:readtags:writerules:readrules:writeManage tags plus automation and billing rules. Inline new tags require tags:write.
Invoicesinvoices:readinvoices:writeinvoices:sendManage invoices, adjustments, payments, attachments, previews, sends, and receipt dispatches.
Expensesexpenses:readexpenses:writeRead and manage expense records, expense lines, and expense attachments.
Reportsreports:readRun task, time, income, tax, receivables, billing, and client-health reports.
Settingssettings:readsettings:writeRead 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.

Connect your app to DoubleTime.

Start free, then use the API to bring tasks, time, clients, and invoices into your own workflow.

Use API tokens for private tools, or OAuth when other people need to connect.

DoubleTime LogoDoubleTime Logo

Client work should end in an invoice.

DoubleTime helps solo billers keep work, rates, and invoices connected.

Built by 0x234, an independent software company.

LinkedIn logoX logoX logo
Product
keyboard_arrow_down
  • Billing hub
  • How it works
  • Pricing
  • FAQ
  • Docs


Company
keyboard_arrow_down
  • Blog
  • About
  • Contact


Legal
keyboard_arrow_down
  • Privacy
  • Terms



© DoubleTime. Built to help client work become billable revenue.