docketrouter
DocumentationBrowse
API reference

Timekeeping

Not enabled on this host yet: every timekeeping call returns 503 until launch. The reference below describes the shipped surface so you can build against it ahead of time. It is free; nothing here is metered.

Signed-in lawyers do not need this reference: /time is the screen: derive the week, review the findings, approve, download the file. The API below is the same thing for firms that script it.

Every request you make through the API is already logged with a timestamp and, when you made it on a matter, the matter it belonged to. Timekeeping turns that log into time entries: it sorts your requests, splits them wherever you went idle for more than fifteen minutes, and records each run as one draft entry whose minutes are the span of the requests it observed, with a narrative stating what those requests were. You edit, approve and export.

What it captures is work done here. It does not read your calendar, your email or your documents, and a derived entry is a floor on the time you spent on a matter on this platform, never an estimate of time spent elsewhere. That is why every derived entry starts as a draft.

POST/time/derivekey or sessionTurn your uncovered requests in a window into draft entries.
GET/timekey or sessionList entries; format=csv or format=ledes exports them.
POST/timekey or sessionAdd a manual entry.
PATCH/time/:idkey or sessionReassign, reword, adjust minutes, approve, mark exported.
DELETE/time/:idkey or sessionRemove an entry; its requests become derivable again.
POST/time/eventskey or sessionRecord captured activity from an extension, add-in or sync.
GET/time/reviewkey or sessionPre-bill review: billing-guideline findings, totals per matter, ready-to-bill.
GET/matters/:id/guidelineskey or sessionA matter's stored billing guidelines and the effective set.
PUT/matters/:id/guidelineskey or sessionReplace a matter's billing guidelines.
GET/time/cliokey or sessionClio connection status and matter links.
GET/time/clio/connectkey or sessionStart connecting Clio (OAuth authorization URL).
GET/time/clio/callbacksessionClio's redirect target.
GET/time/clio/matterskey or sessionThe firm's Clio matters, for mapping.
PUT/time/clio/linkskey or sessionLink one of your matters to a Clio matter.
POST/time/clio/pushkey or sessionPush approved entries to Clio as TimeEntry activities.
DELETE/time/cliokey or sessionDisconnect Clio.

POST /time/derive

FieldTypeNotes
fromISO datetime, optionalDefaults to seven days ago.
toISO datetime, optionalDefaults to now.
idle_gap_mininteger 1–240, optionalA gap longer than this starts a new session. Default 15.
include_docketboolean, optionalAlso suggest an entry for each new filing delivered to one of your docket watches. Default true.
docket_default_mininteger, optionalMinutes a docket suggestion starts with. Default 6. It is a prompt, not an observation; confirm it.
201 Created
{
  "object": "time.derive",
  "window": { "from": "2026-09-29T14:00:00.000Z", "to": "2026-10-06T14:00:00.000Z" },
  "created": 3,
  "requests_covered": 11,
  "docket_suggestions": 1,
  "data": [
    { "id": "te_8f1c2a9d4b7e6c0a1d23", "object": "time.entry", "source": "derived", "status": "draft",
      "matter_id": "9b1c2e4a-…", "matter_title": "Alvarez v. Gulf Coast Rig Servs.",
      "started_at": "2026-10-06T14:00:00.000Z", "ended_at": "2026-10-06T14:23:08.000Z",
      "minutes": 24, "hours_billable": 0.4,
      "narrative": "Alvarez v. Gulf Coast Rig Servs.: Legal research and analysis on DocketRouter, 5 research queries, 2 citation checks, 1 legal analysis.",
      "activity": { "requests": 8, "request_ids": ["req_4f1e…", …], "by_kind": { "research": 5, "citation check": 2, "legal analysis": 1 } },
      "docket_id": null, "created_at": "…", "updated_at": "…", "exported_at": null },
    …
  ]
}

Idempotent. A request belongs to at most one entry, so running derive over the same window again creates nothing and reports created: 0. Delete an entry and its requests are free to be derived again. Requests made on different matters are never merged into one session, even when they interleave in time.

How minutes are counted

A session starts at its first request and ends when its last request's response was observed (the request time plus its latency). A lone request is one minute, never zero. Nothing is added for time you were not making requests. Rounding to a billing increment happens at export, not here: the stored minutes are the true span.

GET /time

QueryDefaultMeaning
from30 days agoEntries whose start is at or after this.
tonowEntries whose start is at or before this.
matteranyOnly entries on this matter id.
statusanydraft, approved or exported.
sourceanyderived, manual or docket.
formatjsoncsv, ledes or clio downloads a file instead.
clio_matter, clio_userblankClio only. clio_matter is required: the matter's exact Clio display number. clio_user is the timekeeper's first and last name as Clio has it.
increment6Billing increment in minutes for hours_billable. 6 is a tenth of an hour; 15 is a quarter.
rate0LEDES only: hourly rate. Line totals are hours × rate. With no rate the file still imports as time.
client_id, timekeeper, timekeeper_name, law_firm_id, invoiceblankLEDES only: the identifiers your billing system expects on each line.
mark_exported0LEDES only: 1 sets every exported entry to exported after the file is produced.
200 OK
{
  "object": "list",
  "window": { "from": "…", "to": "…" },
  "data": [ { "id": "te_…", "object": "time.entry", … , "hours_billable": 0.4 }, … ],
  "total": 14,
  "totals": { "entries": 14, "minutes": 388, "hours_billable": 6.9 },
  "increment_min": 6
}

CSV

GET /time?format=csv
date,started_at,ended_at,minutes,hours_billable,matter_id,matter_title,status,source,narrative,entry_id
2026-10-06,2026-10-06T14:00:00.000Z,2026-10-06T14:23:08.000Z,24,0.4,9b1c2e4a-…,Alvarez v. Gulf Coast Rig Servs.,draft,derived,"Alvarez v. Gulf Coast Rig Servs.: Legal research and analysis on DocketRouter, 5 research queries, 2 citation checks, 1 legal analysis.",te_8f1c…

LEDES 1998B

GET /time?format=ledes&rate=400&client_id=C001&timekeeper=ZL
LEDES1998B[]
INVOICE_DATE|INVOICE_NUMBER|CLIENT_ID|LAW_FIRM_MATTER_ID|INVOICE_TOTAL|BILLING_START_DATE|BILLING_END_DATE|INVOICE_DESCRIPTION|LINE_ITEM_NUMBER|EXP/FEE/INV_ADJ_TYPE|LINE_ITEM_NUMBER_OF_UNITS|LINE_ITEM_ADJUSTMENT_AMOUNT|LINE_ITEM_TOTAL|LINE_ITEM_DATE|LINE_ITEM_TASK_CODE|LINE_ITEM_EXPENSE_CODE|LINE_ITEM_ACTIVITY_CODE|TIMEKEEPER_ID|LINE_ITEM_DESCRIPTION|LAW_FIRM_ID|LINE_ITEM_UNIT_COST|TIMEKEEPER_NAME|TIMEKEEPER_CLASSIFICATION|CLIENT_MATTER_ID[]
20261006|DR-20261006|C001|9b1c2e4a-…|160.00|20260929|20261006|DocketRouter time entries|1|F|0.40|0.00|160.00|20261006|||A102|ZL|Alvarez v. Gulf Coast Rig Servs.: Legal research and analysis on DocketRouter, 5 research queries, 2 citation checks, 1 legal analysis.||400.00|||9b1c2e4a-…[]

Clio

GET /time?format=clio&matter=9b1c2e4a-…&clio_matter=00042-Okafor&rate=350&clio_user=Zach%20Lawless
matter,date,activity_description,note,price,quantity,type,activity_user,non_billable
00042-Okafor,10/06/2026,,"Telephone conference with client re: HOA demand letter",350.00,0.50,TimeEntry,Zach Lawless,

This is Clio Manage's own "Activities from CSV" template, header verbatim, minus the UTBMS columns Clio says to delete when a firm does not use codes. Import it at Clio → Imports → Add → Activities from CSV. Clio matches matter to its display number exactly and requires a price on every row, which is why a Clio file covers one matter at a time and clio_matter and rate are required rather than guessed. Dates are MM/DD/YYYY and quantity is decimal hours at your increment; activity_description is left blank because a non-matching value makes Clio create a new category.

LEDES 1998B is the e-billing layout most firm billing systems import. Activity codes are set from how the entry arose: A102 (research) for derived entries, A104 (review/analyze) for docket suggestions, blank for manual entries. Task codes depend on the matter phase and are left blank for you to set in your system. Pipes and line breaks are stripped from descriptions so every record stays parseable.

POST /time

FieldTypeNotes
started_atISO datetimeRequired.
ended_atISO datetimeOr minutes. One of the two is required.
minutesinteger 1–1440Or ended_at.
narrativestring, 1–4000Required.
matter_idstring, optionalMust be one of your matters; anything else is 404.
201 Created
{ "id": "te_…", "object": "time.entry", "source": "manual", "status": "draft", "minutes": 25, "hours_billable": 0.5, … }

PATCH /time/:id and DELETE /time/:id

FieldTypeMeaning
matter_idstring or nullReassign to one of your matters, or unlink.
narrativestringReword.
minutesintegerAdjust. The derived span is a floor; raise it when you did more than the requests show.
statusdraft | approved | exportedMarking exported stamps exported_at.
started_at, ended_atISO datetimeMove the entry.

At least one field is required. DELETE returns { "ok": true, "id": "te_…" } and releases the entry's requests, so a later derive can pick them up again.

A week, in four calls

# 1. derive drafts from this week's work
curl -s https://docketrouter.ai/api/v1/time/derive -H "Authorization: Bearer dr-…" -H "content-type: application/json" -d '{}' \
  | jq '.data[] | {id, matter_title, minutes, narrative}'

# 2. fix one up and approve it
curl -s -X PATCH https://docketrouter.ai/api/v1/time/te_8f1c… -H "Authorization: Bearer dr-…" -H "content-type: application/json" \
  -d '{ "minutes": 36, "narrative": "Research re: anti-indemnity exposure under Ins. Code ch. 151; review of Texas authority", "status": "approved" }'

# 3. add the call that happened off-platform
curl -s https://docketrouter.ai/api/v1/time -H "Authorization: Bearer dr-…" -H "content-type: application/json" \
  -d '{ "matter_id": "9b1c2e4a-…", "started_at": "2026-10-06T16:00:00Z", "minutes": 20, "narrative": "Telephone conference with client re: indemnity carve-outs" }'

# 4. export approved entries as LEDES for the billing system, marking them exported
curl -s "https://docketrouter.ai/api/v1/time?status=approved&format=ledes&rate=400&client_id=C001&timekeeper=ZL&mark_exported=1" \
  -H "Authorization: Bearer dr-…" -o week.ledes.txt

Per-matter billing guidelines

Clients reject invoices for a short, well-known list of reasons, and the list differs by client. PUT /matters/:id/guidelines stores a client's terms on the matter as a partial override of the defaults: the billing increment, the daily hours cap, the minimum narrative length, what counts as block billing, the words that read as vague, whether unassigned time is an error. GET /time/review then lints each matter's entries under its client's set automatically; a query override applies on top for that request only. Send {} to clear.

PUT /matters/9b1c2e4a-…/guidelines
{ "increment_min": 15, "min_narrative_words": 8, "vague_terms": ["review", "attention to", "work on"] }

Clio: connect, map, push

Beyond the CSV above, approved entries can be pushed straight into Clio Manage as time entries. Three steps, once:

StepCallWhat happens
ConnectGET /time/clio/connectReturns Clio's OAuth URL, state-bound to your account for ten minutes. Clio shows the permissions the app registered (Clio has no scope parameter). On consent Clio redirects to /time/clio/callback, which stores a 30-day token and a refresh token, sealed at rest.
MapPUT /time/clio/linksClio matches on its matter id, so link each of your matters once. GET /time/clio/matters lists the firm's Clio matters to pick from.
PushPOST /time/clio/pushSends approved entries as Activities of type TimeEntry, carrying date, quantity in seconds, note and matter. Already-pushed entries are skipped, so re-running is safe; pushed entries become exported. dry_run shows the exact bodies without calling Clio.
Host setup

Clio endpoints answer 503 until the host carries CLIO_CLIENT_ID, CLIO_CLIENT_SECRET and CLIO_REDIRECT_URI from a developer app registered at developers.clio.com, which needs a paid Clio login. Private apps need no Clio review. US region by default; set CLIO_REGION to eu, ca or au for a firm on another data centre (Clio's app ids are per region).

Errors and limits

SituationStatusBody
Timekeeping not enabled on this host503{"error":{"message":"timekeeping is not enabled on this host (TIMEKEEPING_ENABLED is unset)",…}}
No key and no session401{"error":{"message":"API key or sign-in required",…}}
Entry id you do not own, or that does not exist404{"error":{"message":"time entry not found",…}}
A matter_id that is not yours404{"error":{"message":"matter not found",…}}
Bad body or query400{"error":{"message":"…","type":"invalid_request_error"}}

An entry or matter you do not own answers 404, not 403, so ids cannot be probed.

What this is not

Timekeeping here covers work done on this platform. It does not integrate with Clio, PracticePanther or any other billing system beyond CSV and LEDES export, and it does not capture email, drafting in Word, calls or court time. Those are the hard parts of any timekeeper; add them manually with POST /time until they exist.

Something here wrong or missing? Mail hello@docketrouter.ai with the request_id and we will fix the docs or the API, whichever is broken.