Read Billy invoices, bills, contacts, products and postings; create contacts and draft invoices.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
WRITE: create ONE new contact (customer and/or supplier) in the company's Billy account. It does not check for duplicates (call billy_list_contacts first) and nothing is sent to the contact. Not idempotent: a repeat call creates a second contact. POST /contacts.
Company or person name.
EAN location number for e-invoicing (Denmark).
City.
company (default) or person.
Primary contact e-mail (stored as the primary contact person; nothing is sent).
Phone number.
Street and number.
Postal code.
Your own customer or supplier number.
ISO country code (default DK).
Customer flag (default true).
Supplier flag (default false).
CVR number (Denmark) or EU VAT number.
The server marks this tool as destructive. Running it may delete or overwrite data on the server or in any account you connected with your headers.
WRITE: create ONE new contact (customer and/or supplier) in the company's Billy account. It does not check for duplicates (call billy_list_contacts first) and nothing is sent to the contact. Not idempotent: a repeat call creates a second contact. POST /contacts.
| Argument | Type | Description |
|---|---|---|
| ean | string | EAN location number for e-invoicing (Denmark). |
| city | string | City. |
| name* | string | Company or person name. |
| type | string | company (default) or person. |
| string | Primary contact e-mail (stored as the primary contact person; nothing is sent). | |
| phone | string | Phone number. |
| street | string | Street and number. |
| zipcode | string | Postal code. |
| contact_no | string | Your own customer or supplier number. |
| country_id | string | ISO country code (default DK). |
| is_customer | boolean | Customer flag (default true). |
| is_supplier | boolean | Supplier flag (default false). |
| registration_no | string | CVR number (Denmark) or EU VAT number. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"name"
],
"properties": {
"ean": {
"type": "string",
"maxLength": 20,
"description": "EAN location number for e-invoicing (Denmark)."
},
"city": {
"type": "string",
"maxLength": 255,
"description": "City."
},
"name": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"description": "Company or person name."
},
"type": {
"enum": [
"company",
"person"
],
"type": "string",
"description": "company (default) or person."
},
"email": {
"type": "string",
"format": "email",
"pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
"maxLength": 255,
"description": "Primary contact e-mail (stored as the primary contact person; nothing is sent)."
},
"phone": {
"type": "string",
"maxLength": 50,
"description": "Phone number."
},
"street": {
"type": "string",
"maxLength": 255,
"description": "Street and number."
},
"zipcode": {
"type": "string",
"maxLength": 20,
"description": "Postal code."
},
"contact_no": {
"type": "string",
"maxLength": 50,
"description": "Your own customer or supplier number."
},
"country_id": {
"type": "string",
"pattern": "^[A-Z]{2}$",
"description": "ISO country code (default DK)."
},
"is_customer": {
"type": "boolean",
"description": "Customer flag (default true)."
},
"is_supplier": {
"type": "boolean",
"description": "Supplier flag (default false)."
},
"registration_no": {
"type": "string",
"maxLength": 50,
"description": "CVR number (Denmark) or EU VAT number."
}
}
}WRITE: create ONE invoice in the DRAFT state for an existing contact. The invoice is NOT approved, booked, numbered into the ledger or sent: the user reviews it in Billy and approves and sends it there. The invoice number is assigned by Billy. No payment is created. Not idempotent: a repeat call creates a second draft. POST /invoices with state=draft.
| Argument | Type | Description |
|---|---|---|
| lines* | array | 1-100 invoice lines. |
| tax_mode | string | Whether unit prices include (incl) or exclude (excl) VAT (default: the company's setting). |
| contact_id* | string | The customer's contact id (from billy_list_contacts or billy_create_contact). |
| entry_date | string | Invoice date yyyy-mm-dd (default today, UTC). |
| currency_id | string | Invoice currency, e.g. DKK (default: the company's base currency). |
| contact_message | string | Message to the customer shown at the top of the invoice PDF. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"contact_id",
"lines"
],
"properties": {
"lines": {
"type": "array",
"items": {
"type": "object",
"required": [
"product_id",
"unit_price"
],
"properties": {
"quantity": {
"type": "number",
"maximum": 1000000000,
"description": "Quantity (default 1).",
"exclusiveMinimum": 0
},
"product_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$",
"description": "The product id (from billy_list_products)."
},
"unit_price": {
"type": "number",
"maximum": 1000000000,
"minimum": -1000000000,
"description": "Price per unit in the invoice currency (VAT excluded unless tax_mode is incl)."
},
"description": {
"type": "string",
"maxLength": 2000,
"description": "Text shown under the product name on the invoice."
}
}
},
"maxItems": 100,
"minItems": 1,
"description": "1-100 invoice lines."
},
"tax_mode": {
"enum": [
"incl",
"excl"
],
"type": "string",
"description": "Whether unit prices include (incl) or exclude (excl) VAT (default: the company's setting)."
},
"contact_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$",
"description": "The customer's contact id (from billy_list_contacts or billy_create_contact)."
},
"entry_date": {
"type": "string",
"description": "Invoice date yyyy-mm-dd (default today, UTC)."
},
"currency_id": {
"type": "string",
"pattern": "^[A-Z]{3}$",
"description": "Invoice currency, e.g. DKK (default: the company's base currency)."
},
"contact_message": {
"type": "string",
"maxLength": 2000,
"description": "Message to the customer shown at the top of the invoice PDF."
}
}
}One supplier bill by id with its lines. GET /bills/{id}.
| Argument | Type | Description |
|---|---|---|
| id* | string | The bill id (from billy_list_bills). |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$",
"description": "The bill id (from billy_list_bills)."
}
}
}One contact by id, with its contact persons (names and e-mail addresses). GET /contacts/{id}.
| Argument | Type | Description |
|---|---|---|
| id* | string | The contact id (from billy_list_contacts). |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$",
"description": "The contact id (from billy_list_contacts)."
}
}
}One invoice by id with its lines (product, description, quantity, unit price, amount, tax) and its payment associations. GET /invoices/{id}.
| Argument | Type | Description |
|---|---|---|
| id* | string | The invoice id (from billy_list_invoices). |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$",
"description": "The invoice id (from billy_list_invoices)."
}
}
}The Billy company the access token belongs to: id, name, address, registration (CVR) number, base currency, VAT settings, fiscal year, default payment terms, invoice numbering, default accounts. Good first call. GET /organization.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}One product by id with its unit prices per currency. GET /products/{id}.
| Argument | Type | Description |
|---|---|---|
| id* | string | The product id (from billy_list_products). |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$",
"description": "The product id (from billy_list_products)."
}
}
}The chart of accounts: account number, name, group, nature, VAT rate, bank-account flag and bank details (IBAN, bank name) of bank accounts. With is_bank_account=true this lists the bank accounts whose ids billy_list_bank_lines needs. Paged. GET /accounts.
| Argument | Type | Description |
|---|---|---|
| page | integer | Page number, 1-based (default 1). Use next_page from the previous reply. |
| page_size | integer | Records per page, 1-200 (default 50). |
| is_bank_account | boolean | true = only bank accounts. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"page": {
"type": "integer",
"maximum": 100000,
"minimum": 1,
"description": "Page number, 1-based (default 1). Use next_page from the previous reply."
},
"page_size": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Records per page, 1-200 (default 50)."
},
"is_bank_account": {
"type": "boolean",
"description": "true = only bank accounts."
}
}
}List the bank-statement lines of ONE bank account (read only; nothing is matched or reconciled): date, description, amount, side (debit/credit), reconciled flag, receipt state. Get the account id from billy_list_accounts with is_bank_account=true. Paged. GET /bankLines.
| Argument | Type | Description |
|---|---|---|
| q | string | Free-text search over description or amount. |
| page | integer | Page number, 1-based (default 1). Use next_page from the previous reply. |
| side | string | Only debit (money in) or credit (money out) lines. |
| status | string | Line status (the API defaults to booked). |
| page_size | integer | Records per page, 1-200 (default 50). |
| account_id* | string | The bank account id (required). |
| max_amount | number | Amount at most. |
| min_amount | number | Amount at least. |
| is_reconciled |
List bills (purchase invoices from suppliers): supplier, dates, state (draft/approved/voided), amount, balance, isPaid, supplier invoice number. Filter by state, supplier, dates or paid status. Paged. GET /bills.
| Argument | Type | Description |
|---|---|---|
| q | string | Free-text search over supplier name, description, voucher number, amount, supplier invoice number. |
| page | integer | Page number, 1-based (default 1). Use next_page from the previous reply. |
| state | string | Only bills in this state. |
| is_paid | boolean | true = only paid bills, false = only unpaid. |
| page_size | integer | Records per page, 1-200 (default 50). |
| contact_id | string | Only bills of this supplier contact. |
| max_due_date | string | Due date on or before (yyyy-mm-dd). |
| min_due_date | string | Due date on or after (yyyy-mm-dd). |
| sort_property | string |
List customers and suppliers: name, type, address, CVR/registration number, EAN, payment terms, customer/supplier flags. The API has no documented search filter, so `search` is applied to the returned page only (case-insensitive on name, contact number and registration number); page on with next_page to look further. Paged. GET /contacts.
| Argument | Type | Description |
|---|---|---|
| page | integer | Page number, 1-based (default 1). Use next_page from the previous reply. |
| search | string | Keep only contacts on the returned page whose name, contactNo or registrationNo contains this text. |
| page_size | integer | Records per page, 1-200 (default 50). |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"page": {
"type": "integer",
"maximum": 100000,
"minimum": 1,
"description": "Page number, 1-based (default 1). Use next_page from the previous reply."
},
"search": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Keep only contacts on the returned page whose name, contactNo or registrationNo contains this text."
},
"page_size": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Records per page, 1-200 (default 50)."
}
}
}List sales invoices and credit notes (newest first by default): number, date, due date, state (draft/approved/voided), amount, tax, balance, isPaid, contactId, currency. Filter by state, customer, number, date, period, paid status or free text. Paged. GET /invoices.
| Argument | Type | Description |
|---|---|---|
| q | string | Free-text search over invoice number, order number, contact name, line description, amount. |
| page | integer | Page number, 1-based (default 1). Use next_page from the previous reply. |
| state | string | Only invoices in this state. |
| is_paid | boolean | true = only paid invoices, false = only unpaid. |
| page_size | integer | Records per page, 1-200 (default 50). |
| contact_id | string | Only invoices of this contact (from billy_list_contacts). |
| invoice_no | string | Exact invoice number. |
| max_due_date | string | Due date on or before (yyyy-mm-dd); with is_paid=false this finds overdue invoices. |
List ledger postings (the bookkeeping entries): date, text, account, amount, side (debit/credit), currency, voided flag. Filter by account, date or period. Read only. Paged. GET /postings.
| Argument | Type | Description |
|---|---|---|
| q | string | Free-text search over the posting text and the originator name. |
| page | integer | Page number, 1-based (default 1). Use next_page from the previous reply. |
| is_voided | boolean | true = only voided postings, false = only live ones. |
| page_size | integer | Records per page, 1-200 (default 50). |
| account_id | string | Only postings on this account (from billy_list_accounts). |
| max_entry_date | string | Entry date on or before (yyyy-mm-dd). |
| min_entry_date | string | Entry date on or after (yyyy-mm-dd). |
| entry_date_period | string | Date period: all, year:2026, month:2026-03, quarter:2026-2, dates:2026-01-01...2026-01-31 ... |
List products and services usable as invoice lines: name, product number, description, sales account, archived flag. The API has no documented search filter, so `search` is applied to the returned page only (name, productNo). Paged. GET /products.
| Argument | Type | Description |
|---|---|---|
| page | integer | Page number, 1-based (default 1). Use next_page from the previous reply. |
| search | string | Keep only products on the returned page whose name or productNo contains this text. |
| page_size | integer | Records per page, 1-200 (default 50). |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"page": {
"type": "integer",
"maximum": 100000,
"minimum": 1,
"description": "Page number, 1-based (default 1). Use next_page from the previous reply."
},
"search": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Keep only products on the returned page whose name or productNo contains this text."
},
"page_size": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Records per page, 1-200 (default 50)."
}
}
}The company's VAT rates: name, rate (as a fraction, 0.25 = 25 percent), whether it applies to sales or purchases, active flag. Paged. GET /taxRates.
| Argument | Type | Description |
|---|---|---|
| page | integer | Page number, 1-based (default 1). Use next_page from the previous reply. |
| page_size | integer | Records per page, 1-200 (default 50). |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"page": {
"type": "integer",
"maximum": 100000,
"minimum": 1,
"description": "Page number, 1-based (default 1). Use next_page from the previous reply."
},
"page_size": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Records per page, 1-200 (default 50)."
}
}
}| boolean |
| true = only reconciled lines, false = only unreconciled. |
| receipt_state | string | Only lines with a missing or uploaded receipt. |
| sort_property | string | Sort by (default entryDate). |
| max_entry_date | string | Entry date on or before (yyyy-mm-dd). |
| min_entry_date | string | Entry date on or after (yyyy-mm-dd). |
| sort_direction | string | Sort direction (default ASC). |
| entry_date_period | string | Date period: all, year:2026, month:2026-03, quarter:2026-2, dates:2026-01-01...2026-01-31 ... |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"account_id"
],
"properties": {
"q": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Free-text search over description or amount."
},
"page": {
"type": "integer",
"maximum": 100000,
"minimum": 1,
"description": "Page number, 1-based (default 1). Use next_page from the previous reply."
},
"side": {
"enum": [
"debit",
"credit"
],
"type": "string",
"description": "Only debit (money in) or credit (money out) lines."
},
"status": {
"enum": [
"pending",
"booked"
],
"type": "string",
"description": "Line status (the API defaults to booked)."
},
"page_size": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Records per page, 1-200 (default 50)."
},
"account_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$",
"description": "The bank account id (required)."
},
"max_amount": {
"type": "number",
"maximum": 1000000000000,
"minimum": 0,
"description": "Amount at most."
},
"min_amount": {
"type": "number",
"maximum": 1000000000000,
"minimum": 0,
"description": "Amount at least."
},
"is_reconciled": {
"type": "boolean",
"description": "true = only reconciled lines, false = only unreconciled."
},
"receipt_state": {
"enum": [
"missing_receipt",
"receipt_uploaded"
],
"type": "string",
"description": "Only lines with a missing or uploaded receipt."
},
"sort_property": {
"enum": [
"entryDate",
"amount"
],
"type": "string",
"description": "Sort by (default entryDate)."
},
"max_entry_date": {
"type": "string",
"description": "Entry date on or before (yyyy-mm-dd)."
},
"min_entry_date": {
"type": "string",
"description": "Entry date on or after (yyyy-mm-dd)."
},
"sort_direction": {
"enum": [
"ASC",
"DESC"
],
"type": "string",
"description": "Sort direction (default ASC)."
},
"entry_date_period": {
"type": "string",
"pattern": "^[A-Za-z0-9:.,-]{1,80}$",
"description": "Date period: all, year:2026, month:2026-03, quarter:2026-2, dates:2026-01-01...2026-01-31 ..."
}
}
}| Sort by (default entryDate). |
| max_entry_date | string | Bill date on or before (yyyy-mm-dd). |
| min_entry_date | string | Bill date on or after (yyyy-mm-dd). |
| sort_direction | string | Sort direction (default ASC). |
| suppliers_invoice_no | string | Exact supplier invoice number. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"q": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Free-text search over supplier name, description, voucher number, amount, supplier invoice number."
},
"page": {
"type": "integer",
"maximum": 100000,
"minimum": 1,
"description": "Page number, 1-based (default 1). Use next_page from the previous reply."
},
"state": {
"enum": [
"draft",
"approved",
"voided"
],
"type": "string",
"description": "Only bills in this state."
},
"is_paid": {
"type": "boolean",
"description": "true = only paid bills, false = only unpaid."
},
"page_size": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Records per page, 1-200 (default 50)."
},
"contact_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$",
"description": "Only bills of this supplier contact."
},
"max_due_date": {
"type": "string",
"description": "Due date on or before (yyyy-mm-dd)."
},
"min_due_date": {
"type": "string",
"description": "Due date on or after (yyyy-mm-dd)."
},
"sort_property": {
"enum": [
"entryDate",
"dueDate",
"createdTime",
"amount",
"balance",
"contact.name",
"voucherNo",
"suppliersInvoiceNo"
],
"type": "string",
"description": "Sort by (default entryDate)."
},
"max_entry_date": {
"type": "string",
"description": "Bill date on or before (yyyy-mm-dd)."
},
"min_entry_date": {
"type": "string",
"description": "Bill date on or after (yyyy-mm-dd)."
},
"sort_direction": {
"enum": [
"ASC",
"DESC"
],
"type": "string",
"description": "Sort direction (default ASC)."
},
"suppliers_invoice_no": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Exact supplier invoice number."
}
}
}| min_due_date |
| string |
| Due date on or after (yyyy-mm-dd). |
| sort_property | string | Sort by (default entryDate). |
| max_entry_date | string | Invoice date on or before (yyyy-mm-dd). |
| min_entry_date | string | Invoice date on or after (yyyy-mm-dd). |
| sort_direction | string | Sort direction (default ASC). |
| entry_date_period | string | Date period: all, year:2026, month:2026-03, quarter:2026-2, dates:2026-01-01...2026-01-31 ... |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"q": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Free-text search over invoice number, order number, contact name, line description, amount."
},
"page": {
"type": "integer",
"maximum": 100000,
"minimum": 1,
"description": "Page number, 1-based (default 1). Use next_page from the previous reply."
},
"state": {
"enum": [
"draft",
"approved",
"voided"
],
"type": "string",
"description": "Only invoices in this state."
},
"is_paid": {
"type": "boolean",
"description": "true = only paid invoices, false = only unpaid."
},
"page_size": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Records per page, 1-200 (default 50)."
},
"contact_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$",
"description": "Only invoices of this contact (from billy_list_contacts)."
},
"invoice_no": {
"type": "string",
"maxLength": 50,
"minLength": 1,
"description": "Exact invoice number."
},
"max_due_date": {
"type": "string",
"description": "Due date on or before (yyyy-mm-dd); with is_paid=false this finds overdue invoices."
},
"min_due_date": {
"type": "string",
"description": "Due date on or after (yyyy-mm-dd)."
},
"sort_property": {
"enum": [
"entryDate",
"dueDate",
"createdTime",
"invoiceNo",
"amount",
"balance",
"contact.name",
"approvedTime"
],
"type": "string",
"description": "Sort by (default entryDate)."
},
"max_entry_date": {
"type": "string",
"description": "Invoice date on or before (yyyy-mm-dd)."
},
"min_entry_date": {
"type": "string",
"description": "Invoice date on or after (yyyy-mm-dd)."
},
"sort_direction": {
"enum": [
"ASC",
"DESC"
],
"type": "string",
"description": "Sort direction (default ASC)."
},
"entry_date_period": {
"type": "string",
"pattern": "^[A-Za-z0-9:.,-]{1,80}$",
"description": "Date period: all, year:2026, month:2026-03, quarter:2026-2, dates:2026-01-01...2026-01-31 ..."
}
}
}{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"q": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Free-text search over the posting text and the originator name."
},
"page": {
"type": "integer",
"maximum": 100000,
"minimum": 1,
"description": "Page number, 1-based (default 1). Use next_page from the previous reply."
},
"is_voided": {
"type": "boolean",
"description": "true = only voided postings, false = only live ones."
},
"page_size": {
"type": "integer",
"maximum": 200,
"minimum": 1,
"description": "Records per page, 1-200 (default 50)."
},
"account_id": {
"type": "string",
"pattern": "^[A-Za-z0-9_-]{1,64}$",
"description": "Only postings on this account (from billy_list_accounts)."
},
"max_entry_date": {
"type": "string",
"description": "Entry date on or before (yyyy-mm-dd)."
},
"min_entry_date": {
"type": "string",
"description": "Entry date on or after (yyyy-mm-dd)."
},
"entry_date_period": {
"type": "string",
"pattern": "^[A-Za-z0-9:.,-]{1,80}$",
"description": "Date period: all, year:2026, month:2026-03, quarter:2026-2, dates:2026-01-01...2026-01-31 ..."
}
}
}