Draft an invoice from Claude Code

Add Billr to Claude Code with claude mcp add --transport http and a scoped API key. Then ask for the invoice in plain language: Billr always creates it as a draft, and sending is a separate call that needs a key with the billing scope.

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

Who this is for
People who already work inside Claude Code and want the invoice to come out of the same session.
Billr capabilities used
list_clientscreate_invoice

What you need first

  • A Billr account with at least one client. You can create the client in the dashboard, or let the session create it later with create_client.
  • Claude Code installed and able to run the claude command.
  • An API key with the Read and draft profile. That profile can read and write, which is everything this guide needs. It cannot send an invoice to a client.

Set it up

  1. 01Create a scoped key in Billr

    Open Agents → API Keys and create a key with the Read and draft profile. The key is shown once, so copy it before you close the dialog. Keys expire after 90 days and can be revoked at any time. More detail in API keys and scopes.

  2. 02Add Billr as a remote HTTP server

    Claude Code's MCP documentation recommends the HTTP transport for remote servers. Pass the key as an authorization header:

    Example
    claude mcp add --transport http billr https://mcp.billr.cc \
      --header "Authorization: Bearer billr_sk_your_key"

    Replace billr_sk_your_key with the key you just created. If the command prints Added ..., Claude Code wrote the configuration.

  3. 03Keep the key out of shared files

    The command writes to local scope, so the key stays on your machine and applies to this project only. Add --scope user to make Billr available in every project you open; that key still lives in your own Claude Code configuration.

    If a team shares a project .mcp.json, never put the key in it. Claude Code's MCP documentation supports environment variable expansion in command, args, env, url and headers, so the committed file can reference a variable instead:

    Example
    {
      "mcpServers": {
        "billr": {
          "type": "http",
          "url": "https://mcp.billr.cc",
          "headers": { "Authorization": "Bearer ${BILLR_API_KEY}" }
        }
      }
    }

    Each person then exports their own key, for example export BILLR_API_KEY="billr_sk_..." from the shell or secret manager that launches Claude Code. If the variable is missing, the config still loads: Claude Code reports a missing-variable warning for that server and sends the unexpanded text instead. Pick a project-specific name such as BILLR_API_KEY; variable names that Claude Code protects as its own credentials read as empty in remote headers on purpose.

  4. 04Check that Claude Code connected

    Example
    claude mcp list
    claude mcp get billr

    A healthy entry shows a connected status. Inside a session, run /mcp to see the server and the tools it exposes; permission rules address them as mcp__billr__<tool>. If you see a failure status, it means Claude Code could not reach the server, which is what the troubleshooting section below is for.

  5. 05Ask for the invoice in plain language

    Nothing special is needed in the prompt. Name the client, the work and the rate, and say clearly that this is a draft:

    Example
    Draft an invoice for Harbor & Lane: 3 hours of consulting at
    $150 an hour. Leave it as a draft and show me the total.

Worked example

Fictional account, field names as returned by the server

You say

Draft an invoice for Harbor & Lane: 3 hours of consulting at $150 an hour. Leave it as a draft and show me the total.

Your agent calls

  • list_clients { "search": "Harbor" }
  • create_invoice { "request_id": "4b9d2c68-1e5f-4a37-9c82-6d0b8f1a3e54", "client_name": "Harbor & Lane", "line_items": [ { "description": "Consulting", "quantity": 3, "rate": 150 } ] }

Expected response, abridged (field names as the server returns them)

Example
{
  "summary": "Drafted INV-1147 for Harbor & Lane: $450.00",
  "invoice": {
    "id": "6b1f0c62-6a0f-4f38-9d0e-3a1f9a2b7c11",
    "invoice_number": "INV-1147",
    "status": "draft",
    "issue_date": "2026-09-20",
    "due_date": "2026-10-04",
    "currency": "USD",
    "subtotal": 450,
    "discount_amount": 0,
    "tax_amount": 0,
    "shipping_amount": 0,
    "total": 450,
    "balance_due": 450,
    "line_items": [
      {
        "description": "Consulting",
        "quantity": 3,
        "rate": 150,
        "amount": 450
      }
    ]
  },
  "linked_time_entry_ids": [],
  "linked_expense_ids": [],
  "public_url": "https://billr.cc/i/2cQ7f1RtKp",
  "pdf_url": "https://billr.cc/api/public/invoices/2cQ7f1RtKp/pdf",
  "app_url": "https://billr.cc/invoices/6b1f0c62-6a0f-4f38-9d0e-3a1f9a2b7c11"
}

What this flow does not do

  • create_invoice always returns a draft. Nothing is emailed to your client, and no payment link is sent, until someone sends it.
  • Sending is a separate tool, send_invoice. A key with the billing or full profile can send; the Read and draft key used here cannot. That difference is the guardrail in this walkthrough: Billr enforces the key's scope on every call. Human approval is mandatory only for task pricing and task invoice approval, not for ordinary invoice sends.
  • Currency follows the client. Source work recorded in another currency is not converted, so the amounts have to line up before you invoice.
  • A draft keeps its number when you edit it; cancelling and creating a replacement produces a new number. Read Find unbilled work before you let an agent bill from logged time, because attached work cannot be unlinked again while the invoice exists.

Troubleshooting

claude mcp list shows a failed status
Check the URL and the header quoting. The hosted endpoint is https://mcp.billr.cc, and the header must be one argument: --header "Authorization: Bearer billr_sk_...". On a self-hosted install the URL is your own origin plus /api/mcp.
Every tool call answers with a 401
The key was revoked, expired, or copied with a missing character. Create a new key in Agents → API Keys and re-run the add command. Billr answers 401 rather than an empty list, so a bad key never looks like an empty account.
A tool answers "API key is expired, revoked or lacks permission for this tool."
The key is valid but its profile does not include that action. Read and draft covers reading and drafting; sending needs the billing profile, and deleting unbilled work needs the full profile.
Two invoices appear after one retry
create_invoice is idempotent on request_id. If your agent retries after a timeout, it has to reuse the same request_id with the same input to get the original draft back instead of a second one.
The session cannot find the client you meant
client_name has to match the stored name exactly, ignoring case. Ask the agent to run list_clients with a search term first, or pass the client id from the dashboard URL.

Sources

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

You need a Billr account and one client before the session can draft anything. Hosted accounts start on a 14-day trial, and the first client is created from the dashboard or by the agent.

Create an account