Bill a month of logged work

list_unbilled groups billable, uninvoiced time and expenses by client and currency. Passing those ids to create_invoice prices and links the source work atomically, so the draft total is the sum of the exact logged amounts, not a re-typed estimate.

Written and reviewed by the Billr team. Published , last reviewed .

Who this is for
Anyone closing a month from agent-logged work, where the value of the run is not having to re-key a timesheet.
Billr capabilities used
list_unbilledcreate_invoicelist_invoices

What you need first

  • Logged, billable, uninvoiced work. Time without a rate cannot be attached until you set one.
  • A key with the Read and draft profile.
  • A decision about who gets billed for the month, because the run is per client.

Step by step

  1. 01Get the ids for the month you are billing

    list_unbilled has no date filter: it returns everything billable and uninvoiced, with each row carrying its own date. To bill one month, read the date-scoped lists instead and filter to unbilled, billable work. Both page with next_offset, and their totals cover the current page.

    Example
    list_time_entries  {
      "client_name": "Harbor & Lane",
      "from": "2026-09-01",
      "to": "2026-09-30",
      "billed": "unbilled",
      "billable": true
    }
  2. 02Fix anything flagged

    Time entries with no rate are priced at zero and are refused by the invoice builder. list_unbilled marks them with missing_rate: true; set a rate with update_time_entry before billing, which recomputes the amount from hours times rate.

  3. 03Draft one invoice per client

    Pass the client and the exact time_entry_ids and expense_ids you selected for that month. Leave line_items out: the server writes one line per source at the exact amount and includes the tracked seconds in the description. Give each client its own request_id.

    Example
    create_invoice  {
      "request_id": "8f2c1b74-3d5e-4a90-9b17-6e4a2c8d0f31",
      "client_id": "9f2a4d18-3b7c-4a6e-8f0d-1c5b2e9a7d44",
      "issue_date": "2026-09-30",
      "time_entry_ids": ["3f6c1a44-5b2d-4e71-9c08-7a1d3f5b9e22"],
      "expense_ids": ["d41b8e93-2a70-4c15-9b6f-5e8a1c3d7f08"]
    }
  4. 04Confirm what cleared and what is left

    Re-read the same date-scoped lists, or list_unbilled. Attached work disappears because drafting marks it billed immediately, and anything still listed is work you chose not to bill or could not bill yet.

Worked example

Fictional account, field names as returned by the server

You say

Draft the September invoice for Harbor & Lane from time and expenses dated in September. Do not send it.

Your agent calls

  • list_time_entries { "client_name": "Harbor & Lane", "from": "2026-09-01", "to": "2026-09-30", "billed": "unbilled", "billable": true }
  • list_expenses { "client_name": "Harbor & Lane", "from": "2026-09-01", "to": "2026-09-30", "billed": "unbilled", "billable": true }
  • create_invoice { "request_id": "8f2c1b74-3d5e-4a90-9b17-6e4a2c8d0f31", "client_name": "Harbor & Lane", "issue_date": "2026-09-30", "time_entry_ids": [ "3f6c1a44-5b2d-4e71-9c08-7a1d3f5b9e22" ], "expense_ids": [ "d41b8e93-2a70-4c15-9b6f-5e8a1c3d7f08" ] }

Expected response, abridged

Example
{
  "summary": "Drafted INV-1148 for Harbor & Lane: $272.50 Source work was priced and linked atomically.",
  "invoice": {
    "invoice_number": "INV-1148",
    "status": "draft",
    "total": 272.5,
    "line_items": [
      {
        "description": "Schema markup fixes (5400 seconds)",
        "quantity": 1,
        "rate": 225,
        "amount": 225
      },
      {
        "description": "Client lunch",
        "quantity": 1,
        "rate": 47.5,
        "amount": 47.5
      }
    ]
  },
  "linked_time_entry_ids": [
    "3f6c1a44-5b2d-4e71-9c08-7a1d3f5b9e22"
  ],
  "linked_expense_ids": [
    "d41b8e93-2a70-4c15-9b6f-5e8a1c3d7f08"
  ]
}

Limits and gotchas

  • list_unbilled has no date filter, so a month-scoped run starts from list_time_entries and list_expenses with from and to, or from reading dates in the unbilled list.
  • Source-linked work must match the invoice client and currency. Cross-currency time cannot be attached.
  • Once attached, the work is immutable while the invoice exists. Cancelling does not release it, and there is no supported unlink step.
  • Each source id can be billed once. Passing an id already on another draft fails the whole call.
  • Every list pages and its totals apply to the current page, so follow next_offset until it is null before trusting a month-level number.

Troubleshooting

"Time must be unbilled, billable, priced and assigned to this client"
One of the time entry ids is missing a rate, is non-billable, is already billed, or belongs to another client. Re-read list_unbilled for that client and fix the entry with update_time_entry first.
The draft total is lower than the unbilled list showed
Some work stayed behind. Entries without a client stay in unassigned and cannot ride on a client invoice; assign the client first.
A client was missed in the run
Compare the date-scoped lists with the clients you iterated. Drafting removes work immediately, so a client lacking a draft still appears with its full set of entries.

Sources

Reviewed 2026-09-20 against Billr's own tool implementations and the client documentation linked above.

The draft is still editable, so correct it before sending. Sending is a separate call with its own scope.

Review the drafts