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
claudecommand. - 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
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.
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:
claude mcp add --transport http billr https://mcp.billr.cc \ --header "Authorization: Bearer billr_sk_your_key"Replace
billr_sk_your_keywith the key you just created. If the command printsAdded ..., Claude Code wrote the configuration.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 userto 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 incommand,args,env,urlandheaders, so the committed file can reference a variable instead:{ "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 asBILLR_API_KEY; variable names that Claude Code protects as its own credentials read as empty in remote headers on purpose.04Check that Claude Code connected
claude mcp list claude mcp get billrA healthy entry shows a connected status. Inside a session, run
/mcpto see the server and the tools it exposes; permission rules address them asmcp__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.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:
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)
{
"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_invoicealways 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
- Claude Code MCP documentation
- Claude Code permissions and tool names
- Billr: connect your agent
- Billr: API keys and scopes
Reviewed 2026-09-20 against Billr's own tool implementations and the client documentation linked above.
Read next
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