blesta-mcp
Summary: blesta-mcp gives LLM agents full read/write access to a Blesta billing install through ~50 purpose-built MCP tools plus a generic blesta_call escape hatch to any public Blesta model method.
Find and inspect customers: free-text client search (email, name, company, client number), full client records with settings, contacts and phone numbers, staff/sticky notes, and per-currency balances with invoice status counts.
Work with invoices: search or list by client/status, read invoice details and line items, list applied payments, create invoices with line items, edit headers (including void), edit/add/remove lines, delete drafts, merge, split, append services, set closed, and email-deliver them.
Get customers paid: build no-login "pay now" links (
create_invoice_payment_link), verify hashes/sid tokens and whether payment is still due, download invoice PDFs (via the Component API plugin), record offline payments, apply existing credit, and charge stored cards/bank accounts withprocess_payment(off unlessBLESTA_ALLOW_PAYMENTS=1).Manage services: list a client's services with renewal dates/prices, search services across clients by domain/username, view one service in full (options, pricing, children, pending changes), list allowed actions, suspend/unsuspend/cancel/uncancel, create, delete, upgrade/downgrade packages, move between clients, update status/dates/price, set module fields, invoice services, and process or cancel queued service changes.
Review money movement: client and global transaction history with filters (status, type, date/amount ranges, applied status), single transactions plus the invoices they paid, and masked card/bank accounts on file.
Browse the catalog and sales side: packages and groups with optional pricing, individual package details, quotations with line items and resulting invoices, and coupon validation against packages.
Maintain client records: add staff notes, set status (active/inactive/fraud), update names/email/company/address, add or change phone/fax numbers, add or delete contacts, and change language, currency, tax ID/exemption, invoice method and invoice contact.
Reach anything else:
blesta_callinvokes any{model}/{method}(including plugin models) with named parameters, nested arrays, verb inference or override, and a response size cap.Safety controls: write tools are refused when
BLESTA_READ_ONLY=1(payment-link encrypt/decrypt still works),process_paymentrequiresBLESTA_ALLOW_PAYMENTS=1, card numbers are never returned, anddry_runvalidates several write operations without saving.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@blesta-mcp Find client by email and list their unpaid invoices"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
blesta-mcp
An MCP server that exposes the Blesta billing API to LLM agents (Claude Code, Claude Desktop, Hermes, or any MCP client).
It ships a small set of purpose-built, read-only tools for the common support workflow (find a customer, look at their invoices, services and payments, hand them a pay link) plus a generic blesta_call escape hatch that can reach every public model method Blesta exposes.
Tools
Tool | What it does | Blesta methods |
| Find customers by email, name, company, client number or phone number |
|
| Full client record, optionally with effective settings |
|
| Free-text invoice search, or list a client's invoices by status |
|
| Invoice details, line items, totals, paid/due |
|
| Transactions applied to an invoice and their status |
|
| A client's services with status, renewal date and price; or one service in full |
|
| Build a no-login "pay now" URL for one invoice |
|
| Validate a hash or |
|
| Contacts under a client with phone numbers |
|
| Staff notes and sticky notes |
|
| Amount due per currency and invoice counts by status |
|
| Payment history with filters |
|
| One payment plus applied invoices |
|
| Masked cards/bank accounts on file |
|
| Find a service by domain/username/number across clients |
|
| Operations Blesta allows on a service right now |
|
| Product catalog with prices |
|
| Quotes |
|
| Validate a promo code |
|
| Log an interaction on the account |
|
| active / inactive / fraud |
|
| Names, email, company, address of a contact; keeps the login in step with the email; |
|
| Add, change, remove phone/fax numbers |
|
| Add a billing or other contact |
|
| Remove an additional contact |
|
| Language, currency, tax ID, tax exemption, invoice method, invoice contact |
|
| New invoice with line items |
|
| Queue (re)delivery by email |
|
| Header fields incl. void |
|
| Add, update, remove line items |
|
| Delete a draft invoice |
|
| Combine open invoices into one |
|
| Move lines to a new invoice |
|
| Add service lines priced by Blesta |
|
| Recalculate closed state |
|
| Service lifecycle |
|
| Full service view: pricing info, options, next invoice date, children, actions, pending changes |
|
| Upgrade/downgrade targets with pricing terms |
|
| Add a service, optionally provision on the module and invoice it; |
|
| Delete a pending/in-review/canceled service |
|
| Upgrade, downgrade or term change; |
|
| Status (activate), dates, qty, price override, coupon, module fields |
|
| Reactivate a canceled service |
|
| Transfer a service to another client |
|
| Invoice new or renewing services |
|
| Store a module field on the service |
|
| Process or cancel a queued service change |
|
| Record offline payment and apply it |
|
| Render invoices to PDF with Blesta's template and save the file (needs the Component API plugin) |
|
| Record offline payment for one invoice by invoice ID |
|
| Apply existing credit to invoices |
|
| Charge a stored payment account |
|
| Call any | anything in source-docs.blesta.com |
Write tools are refused when BLESTA_READ_ONLY=1. process_payment additionally needs BLESTA_ALLOW_PAYMENTS=1.
The payment link is built exactly the way Blesta's own payment-reminder emails build it:
{install}/client/pay/method/{invoice_id}/?sid=rawurlencode(systemEncrypt("c={client_id}|h={hash}"))so the resulting URL opens Blesta's native client-area payment page without a login.
Related MCP server: WHMCS MCP Server
Requirements
Node.js 20 or newer
A Blesta API user + key (Blesta admin: Settings > System > API Access)
HTTPS access to the Blesta install (the API key travels in every request)
Install
git clone <this repo> blesta-mcp
cd blesta-mcp
npm install
npm run buildConfiguration
All configuration is by environment variable.
Variable | Required | Meaning |
| yes | Install URL, e.g. |
| yes | API user name |
| yes | API key |
| no |
|
| on IonCube installs |
|
| alternative | Path to a file containing only the system key (preferred over the env var: keep the file |
| no | Staff member ID recorded on notes, suspensions and payments when a tool call gives none |
| no | Company for catalog calls; default is the first company Blesta reports |
| no |
|
| no | Where |
| no | Cache file for the phone index used by |
| no | Age after which the phone index is rebuilt in the background; default 24 |
| no | Parallel read calls while building the phone index; default 8, max 32 |
| no | Base URL used in customer-facing links when it differs from |
| no | Client-area path used in payment links, default |
| no | HTTP timeout, default |
| no |
|
Security note. Blesta's API has no scopes: any valid key can call every public model method, which is full administrative access. Keep the key out of shell history and prompts, prefer
BLESTA_READ_ONLY=1unless the agent must write, and never point this server at an install over plain HTTP.
Add to Claude Code
claude mcp add blesta \
-e BLESTA_URL=https://billing.example.com \
-e BLESTA_API_USER=apiuser \
-e BLESTA_API_KEY=your-key \
-e BLESTA_READ_ONLY=1 \
-e BLESTA_SYSTEM_KEY_FILE=/home/you/.config/blesta-mcp/system_key \
-- node /absolute/path/to/blesta-mcp/dist/index.jsOr in a project .mcp.json / Claude Desktop claude_desktop_config.json:
{
"mcpServers": {
"blesta": {
"command": "node",
"args": ["/absolute/path/to/blesta-mcp/dist/index.js"],
"env": {
"BLESTA_URL": "https://billing.example.com",
"BLESTA_API_USER": "apiuser",
"BLESTA_API_KEY": "your-key",
"BLESTA_READ_ONLY": "1"
}
}
}
}How requests are made
The client mirrors the official PHP SDK (phillipsdata/blesta_sdk):
URL:
{BLESTA_URL}/api/{model}/{method}.jsonAuth headers:
BLESTA-API-USER,BLESTA-API-KEYParameters are passed by name and encoded like PHP's
http_build_query, so nested arrays becomevars[lines][0][amount]=5.99.GET/DELETEuse the query string,POST/PUTa form-encoded body.A
200response is unwrapped from{"response": ...}; any other status is turned into a tool error that lists Blesta's per-field validation messages.
blesta_call infers the HTTP verb from the method name (get*/search* -> GET, add* -> POST, edit*/set* -> PUT, delete* -> DELETE) and accepts http_method to override it. Plugin models are addressed as plugin.model.
Two Blesta quirks worth knowing:
Timestamps sent to Blesta must include a timezone (
2026-01-31T12:00:00Z); Blesta otherwise assumes the company's local time.On IonCube-encoded installs a call can fail with
Failed to retrieve the default valuewhen an optional argument is omitted. The curated tools omit a few trailing optionals (transactions/getAppliedwithouttransaction_id,services/getListwithoutfilters), soget_invoice_paymentsandget_client_servicesare the first tools to try against a real install; if they return that error, open an issue and the calls will be made fully explicit.create_invoice_payment_linkandverify_invoice_payment_linkstill work underBLESTA_READ_ONLY=1:Encryption.systemEncrypt/systemDecryptare side-effect free and are the only POST calls allowed in that mode.Verified against a live IonCube-encoded 5.x install: all 27 read tools pass, including
download_invoice_pdfand thedry_runpaths ofcreate_service,change_service_packageandupdate_service(they run Blesta's validation without writing). The 30 write tools were verified against a mock only (request paths, names andhttp_build_queryencoding checked against the Blesta model sources); they have not been executed against a live install. A generated payment link opens Blesta's payment-method page without a login onceBLESTA_SYSTEM_KEY/BLESTA_SYSTEM_KEY_FILEis set (see Configuration). Passing an empty key is not equivalent to the default: Blesta uses the empty string literally, so the resultingsidis rejected.
Invoice PDFs
Stock Blesta renders invoice PDFs in the InvoiceDelivery component, which the API does not route. The free Component API plugin from Blesta fixes that: install it once (unzip into plugins/, then Settings > Company > Plugins > Available > Install) and download_invoice_pdf calls InvoiceDelivery.downloadInvoices through it and saves the PDF locally. Without the plugin the tool fails with an explanatory message. No invoice caching setting is needed.
Documentation
docs/tools.md — every tool: inputs, outputs, errors, examples
docs/architecture.md — request flow, encoding rules, how to add a tool
docs/blesta-api-notes.md — Blesta API facts this server relies on, with sources
docs/testing.md — smoke test with a mock, first live calls, troubleshooting
Development
npm run dev # tsc --watch
npm run build
node dist/index.js # speaks MCP over stdioLicense
GPL-3.0-only. See LICENSE.
Available Tools
33 toolsadd_client_noteAdd client noteA
Record a staff note on a client account (e.g. summary of a support interaction). Wraps Clients.addNote. staff_id defaults to BLESTA_STAFF_ID.
Inputs: title (required), client_id (required), sticky, staff_id, +1 more.
apply_transactionApply transaction to invoicesA
Apply an existing transaction's unapplied balance (client credit) to one or more invoices in the same currency. Wraps Transactions.apply.
Inputs: amounts (required), transaction_id (required).
blesta_callCall any Blesta API methodADestructive
Escape hatch: call any public model method of the Blesta API as {model}/{method}. Parameters are passed BY NAME and must match the PHP method's argument names (see https://source-docs.blesta.com/classes/{Model}.html). Nested arrays/objects are supported (e.g. params: {"vars": {"client_id": 1, "lines": [{"description": "x", "amount": "5.00"}]}}). Plugin models use "plugin.model" (e.g. "support_manager.support_manager_tickets"). If http_method is omitted it is inferred from the method name (get*/search* -> GET, add* -> POST, edit*/set* -> PUT, delete* -> DELETE). Timestamps must include a timezone (e.g. 2026-01-31T12:00:00Z). If Blesta answers HTTP 500 'Failed to retrieve the default value', resend with every optional argument explicitly set. Any valid API key has full administrative power; prefer the purpose-built tools when one exists.
Inputs: model (required), method (required), params, max_chars, +1 more.
cancel_serviceCancel serviceADestructive
Cancel a service now, at end of the current term, or on a given date. Wraps Services.cancel. when: 'end_of_term' (default, safest), 'now' (immediate, module deprovisions if use_module), or an ISO 8601 date with timezone. Scheduled cancellations are executed by Blesta's cron and can be undone before then with blesta_call services/unCancel.
Inputs: service_id (required), when, reason, use_module, +1 more.
create_invoiceCreate invoiceA
Create a new invoice for a client with one or more line items. Wraps Invoices.add. Dates default to now (billed) and now+7 days (due). Set deliver_by_email to queue the invoice email (sent by Blesta's cron). Set draft to create it unissued. Returns the new numeric invoice ID and the invoice as stored.
Inputs: lines (required), currency (required), client_id (required), draft, +5 more.
create_invoice_payment_linkCreate invoice payment linkARead-onlyIdempotent
Produce a customer-facing URL that lets the invoice be paid without logging in. Uses Invoices.createPayHash(client_id, invoice_id) then Encryption.systemEncrypt to build the sid token, exactly like Blesta's own payment reminder emails: {install}/client/pay/method/{invoice_id}/?sid=... . The link is tied to this client and invoice and does not expire. Only hand it to the invoice's own customer. Refuses if the invoice does not belong to client_id. On IonCube-encoded installs this call fails with 'Failed to retrieve the default value' unless BLESTA_SYSTEM_KEY is configured.
Inputs: client_id (required), invoice_id (required).
get_clientGet clientARead-onlyIdempotent
Retrieve one customer by numeric client ID: identity, primary contact details (name, email, address, phone numbers), status (active/inactive/fraud), client group and, optionally, effective settings (currency, language, autodebit, ...). Wraps Clients.get.
Inputs: client_id (required), include_settings.
get_client_balanceGet client balanceARead-onlyIdempotent
Amount the client owes in a currency (sum of open invoices) plus invoice counts by status (open, past_due, closed, ...). Wraps Invoices.amountDue and Invoices.getStatusCount. Use get_client with include_settings to learn the client's default currency.
Inputs: currency (required), client_id (required).
get_client_contactsGet client contactsBRead-onlyIdempotent
All contacts under a client (primary, billing, other) with name, email, address and phone/fax numbers. Wraps Contacts.getAll and Contacts.getNumbers.
Inputs: client_id (required), contact_type, include_numbers.
get_client_notesGet client notesARead-onlyIdempotent
Staff notes on a client account, newest first, plus pinned (sticky) notes. Read these before acting on an account. Wraps Clients.getNoteList, Clients.getNoteListCount and Clients.getAllStickyNotes.
Inputs: client_id (required), page.
get_client_servicesGet client servicesARead-onlyIdempotent
List a customer's services (hosting accounts, domains, add-ons) with status, package/pricing, renewal date and price. Wraps Services.getList (paged, default status 'active') and, when include_renewal_price is set, Services.getRenewalPrice per service. Pass service_id instead of client_id to fetch a single service (Services.get), including module fields such as domain/username. Results are compacted by default; set full for the raw Blesta objects.
Inputs: full, page, status, client_id, +3 more.
get_client_transactionsGet client transactionsARead-onlyIdempotent
Payment history for a client (or all clients): amount, currency, type (cc/ach/other), status, gateway, reference and which invoices each payment was applied to. Wraps Transactions.getList / getListCount. Filters: payment_type, reference_id, date and amount ranges, applied_status (fully_applied, partially_applied, not_applied).
Inputs: page, status, end_date, client_id, +6 more.
get_invoiceGet invoiceARead-onlyIdempotent
Read one invoice by numeric invoice ID: displayed number (id_code), client_id, status, date_billed, date_due, date_closed, currency, subtotal, total, paid, due (total minus paid), taxes and line_items with descriptions, quantities and amounts. Wraps Invoices.get. Use search_invoices first if you only know the displayed invoice number.
Inputs: invoice_id (required).
get_invoice_paymentsGet invoice paymentsARead-onlyIdempotent
List payments (transactions) applied to an invoice: applied amount and date, transaction status (approved/declined/void/error/pending/returned), payment type, gateway, reference and transaction number. Wraps Transactions.getApplied(invoice_id). Set include_transaction_details to also fetch each full transaction record (Transactions.get). Also returns the invoice's current paid/due summary.
Inputs: invoice_id (required), include_transaction_details.
get_packageGet packageBRead-onlyIdempotent
One package in full: pricing per term, description, module, configurable option groups, groups. Wraps Packages.get.
Inputs: package_id (required).
get_payment_accountsGet payment accountsARead-onlyIdempotent
Cards and bank accounts a client has on file (masked: last4, type, expiry, holder name, gateway reference). Never returns full numbers. Wraps Accounts.getAllCcByClient and Accounts.getAllAchByClient. Useful for autodebit failures and expired cards.
Inputs: client_id (required).
get_quotationGet quotationARead-onlyIdempotent
One quote with its line items and any invoices generated from it. Wraps Quotations.get, getLineItems, getInvoices.
Inputs: quotation_id (required).
get_service_actionsGet service actionsARead-onlyIdempotent
Which operations Blesta allows on a service in its current state (e.g. suspend, unsuspend, cancel, uncancel, change_renew_date, ...). Wraps Services.get + Services.getActions(current_status).
Inputs: service_id (required).
get_transactionGet transactionARead-onlyIdempotent
One payment in full plus the invoices it was applied to. Wraps Transactions.get and Transactions.getApplied(transaction_id).
Inputs: transaction_id (required).
list_packagesList packagesARead-onlyIdempotent
Product catalog: packages and package groups. Wraps Packages.getAll (compact view) and Packages.getAllGroups. Packages.getAll does not return prices; set include_pricing (max 25 packages per call, so filter by name or group first) to fetch Packages.get for each and attach pricing per term/period/currency. Pass full for raw objects incl. descriptions.
Inputs: full, name, type, status, +4 more.
list_quotationsList quotationsARead-onlyIdempotent
Quotes (estimates) for a client or across all clients, by status. Wraps Quotations.getList / getListCount. Statuses: draft, pending, approved, invoiced, expired, dead, lost, all.
Inputs: page, status, client_id.
lookup_couponLookup couponARead-onlyIdempotent
Validate a promo code: existence, status, start/end dates, usage limits, discount amounts per currency, eligible packages. Wraps Coupons.getByCode; with package_ids, also Coupons.getForPackages to check applicability.
Inputs: code (required), package_ids.
process_paymentCharge payment account on fileADestructive
Charge a client's stored card or bank account through the payment gateway and apply the result to invoices. Wraps Payments.processPayment with account_id only; raw card data is never accepted. Sends the client a receipt unless disabled. Moves real money. DISABLED: set BLESTA_ALLOW_PAYMENTS=1 to enable.
Inputs: type (required), amount (required), currency (required), client_id (required), +4 more.
record_manual_paymentRecord manual paymentA
Record an offline payment (bank transfer, cash, check) and apply it to invoices. Wraps Transactions.add then Transactions.apply. Does not charge anything; it only records money already received. Omit apply_to to leave the amount as unapplied credit.
Inputs: amount (required), currency (required), client_id (required), message, +3 more.
search_clientsSearch clientsARead-onlyIdempotent
Find customers matching a free-text query (email, first/last name, company, client ID code such as '1234'). Wraps Clients.search. Returns a page of client records including the primary contact's name, email and status. Use get_client with the returned id for full details.
Inputs: query (required), page.
search_invoicesSearch invoicesARead-onlyIdempotent
Find invoices two ways. (1) query: free-text search by displayed invoice number, client name/email or line description (wraps Invoices.search). (2) client_id: list a customer's invoices filtered by status (wraps Invoices.getList; default status 'open'). Provide at least one of query or client_id. Each result includes numeric id, displayed id_code, status, dates, total, paid and due amounts.
Inputs: page, query, status, currency, +2 more.
search_servicesSearch servicesARead-onlyIdempotent
Find services across all clients by domain, hostname, username, package name or service number. Wraps Services.search (with search_fields to include module field values such as the domain). Returns compact service records with client_id.
Inputs: query (required), full, page, search_fields.
send_invoiceSend invoiceA
Queue (re)delivery of an invoice to the client by email or another configured method. Wraps Invoices.addDelivery. Blesta's cron performs the actual send within a few minutes; the tool also returns the delivery log so you can see prior sends.
Inputs: invoice_id (required), method.
suspend_serviceSuspend serviceBDestructive
Suspend a service (and, when use_module is true, tell the provisioning module to suspend the account). Wraps Services.suspend.
Inputs: service_id (required), reason, staff_id, use_module.
unsuspend_serviceUnsuspend serviceA
Reactivate a suspended service (and on the module when use_module is true). Wraps Services.unsuspend.
Inputs: service_id (required), staff_id, use_module.
update_client_statusUpdate client statusADestructive
Set a client's status to active, inactive or fraud. Wraps Clients.edit(client_id, {status}). Inactive/fraud clients cannot log in; fraud also blocks orders.
Inputs: status (required), client_id (required).
update_invoiceUpdate invoiceADestructive
Edit invoice header fields: status (active/draft/proforma/void), due date, billed date, notes. Wraps Invoices.edit. Use status: "void" to void an unpaid invoice (irreversible in practice). Line items are not editable here; use blesta_call invoices/edit with lines for that.
Inputs: invoice_id (required), status, date_due, date_billed, +2 more.
verify_invoice_payment_linkVerify invoice payment linkARead-onlyIdempotent
Check that a payment link/hash is valid for a given client and invoice, and report whether payment is still due. Accepts either the raw 16-character hash (with client_id) or the sid token / full payment URL (decrypted via Encryption.systemDecrypt). Wraps Invoices.verifyPayHash and Invoices.get. On IonCube-encoded installs this call fails with 'Failed to retrieve the default value' unless BLESTA_SYSTEM_KEY is configured.
Inputs: invoice_id (required), sid, hash, client_id.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 33 tools
Each tool targets a distinct resource+action combination, even in dense areas like payments (record_manual_payment vs apply_transaction vs process_payment are clearly separated by funding source and behavior). The only generic tool, blesta_call, is explicitly an escape hatch rather than a competing operation.
Tool names overwhelmingly follow a snake_case verb_noun pattern (get_client, create_invoice, suspend_service). Minor deviations like lookup_coupon instead of get_coupon and list_quotations alongside get_quotation are predictable but not perfectly uniform.
33 tools is heavy, but the breadth of Blesta's domain (clients, invoices, services, payments, packages, quotes, coupons) justifies most of them. The count sits above the typical heavy range and some getters could potentially be consolidated, yet no tool feels truly redundant.
Core client, invoice, service, and payment operations are well covered, including search, retrieve, and common state changes. Notable gaps exist (no create_client, no package/quote creation, no direct line-item editing), though blesta_call provides a workaround for advanced operations.
Maintenance
Related MCP Connectors
Pay-per-use tool marketplace for AI agents. Search, price-check, and call APIs via MCP.
Create invoices, clients, and Stripe payment links from any agent. 7 tools. No email or deletes.
Malaysian SME accounting, e-Invoice and payroll for your AI. 77 tools; writes are approved drafts.
Hosted MCP with 91 agent tools: X, domains, SEO, Maps, Trends, Search, YouTube, TikTok, and more.
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables LLMs to interact with Billforward's billing and subscription management API, providing tools for accounts, subscriptions, invoices, payments, and more with read-only safety by default.2331 npm3MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to administrate WHMCS installations through the External API, providing ~50 tools for clients, billing, orders, services, domains, support, and aggregators with safety features and governance.58 npm3ISC
- AlicenseBqualityBmaintenanceEnables AI assistants to access the Billingo v3 invoicing API through MCP, providing 49 read/write tools for managing invoices, partners, and related data over stdio or HTTP transports.2263 npm3MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to query and manage a web hosting billing platform through its External API — clients, tickets, invoices, domains, orders, products, and system data. It defaults to read-only with per-domain opt-in for writes, two-step confirmation for billing and destructive actions, and automatic redaction of secrets, card data, and PII.1MIT