Todah for developers and AI apps
Connect Claude or another AI app to your Todah workspace, or build on the Todah REST API. Both are free on every plan, including Free.
Connect an AI app (MCP)
Todah runs a remote Model Context Protocol server. Any AI app that supports remote MCP servers can use it. Its address is:
https://todah.ca/mcp
Claude
- In Claude, open Settings → Connectors and choose Add custom connector.
- Name it Todah and paste
https://todah.ca/mcpas the address. - Choose Connect. Sign in to Todah, then review what Claude may do. Untick Also let it make changes if you want Claude to read only.
- Ask Claude things like "which invoices are overdue?", "draft an invoice to Acme for two hours of consulting at $120 plus GST", or "how did revenue compare month to month this year?"
In Claude Code: claude mcp add --transport http todah https://todah.ca/mcp, then run /mcp
to sign in.
What an AI app can and cannot do
- It acts as you, with your role in your workspace, and sees only that workspace.
- With read access it can look at clients, invoices, quotes, credit notes, bills, vendors, products, recurring invoices and the reports built from them.
- With change access it can also create and edit clients, vendors, products, draft invoices, quotes and bills, record a payment you have received, and email an invoice or quote to your client. Before any email it must ask you, and at most 30 emails an hour go out this way.
- It can never see bank transactions or bank connections, delete anything, issue a refund, take or move money, or change your plan, team, settings or sign-in.
- Disconnect it at any time in Settings → Connected Apps. It loses access at once.
| Tool | What it does | Access |
|---|---|---|
get_workspace | The signed-in workspace, plan, person and what this connection may do. | Read |
get_overview | Headline numbers: clients, invoices, revenue, outstanding and overdue amounts, expenses. | Read |
list_clients | Clients, newest first. Filter by text in the name or email. | Read |
get_client | One client with its contact people. | Read |
get_client_statement | A client's statement: invoices, payments and credits, with the balance owing. | Read |
list_invoices | Invoices, newest first, with status (Draft, Sent, Overdue, Partially paid, Paid, Cancelled) and balance. | Read |
get_invoice | One invoice with its lines, client and payments. | Read |
list_overdue_invoices | Invoices past their due date with a balance still owing. | Read |
list_quotes | Quotes (estimates), newest first. | Read |
get_quote | One quote with its lines and client. | Read |
list_credit_notes | Credit notes issued to clients. | Read |
list_bills | Bills and receipts from vendors, newest first. | Read |
get_bill | One bill with its lines and expense categories. | Read |
list_overdue_bills | Bills past their due date and still owed. | Read |
list_vendors | Vendors (suppliers). | Read |
list_products | Saved products and services with default prices. | Read |
list_expense_categories | Expense categories for bill lines, each marked COGS or OpEx. | Read |
list_sales_taxes | The sales taxes set up in this workspace, with rates. | Read |
list_recurring_invoices | Invoices set to repeat on a schedule. | Read |
get_report | A financial report built from invoices, bills and credit notes. Some need the Business plan. | Read |
create_client | Add a client. | Change |
update_client | Change a client's details. Fields left out keep their current value. | Change |
create_vendor | Add a vendor (supplier). | Change |
update_vendor | Change a vendor's details. Fields left out keep their current value. | Change |
create_product | Save a product or service with a default price. | Change |
update_product | Change a saved product or service. Fields left out keep their current value. | Change |
create_invoice | Create an invoice as a draft. It is numbered automatically and is not sent. | Change |
update_invoice | Change a draft invoice. Fields left out keep their current value; items, if given, replace all the lines. Sent invoices cannot be edited. | Change |
record_invoice_payment | Record money a client has already paid against an invoice (cash, cheque, e-transfer...). This is bookkeeping: it moves no money. | Change |
create_quote | Create a quote (estimate) as a draft. Not sent. | Change |
update_quote | Change a quote. Fields left out keep their current value; items, if given, replace all the lines. | Change |
convert_quote_to_invoice | Turn a quote into a draft invoice with the same lines. | Change |
create_bill | Record a bill or receipt from a vendor. | Change |
update_bill | Change a bill. Fields left out keep their current value; items, if given, replace all the lines. | Change |
send_invoice | Email an invoice to the client's billing contacts, with the PDF attached. Cannot be undone; requires the user to confirm. | Change: emails a client, asks you first |
send_quote | Email a quote to the client's contacts, with the PDF attached. Cannot be undone; requires the user to confirm. | Change: emails a client, asks you first |
REST API
Base address https://todah.ca/api/v1. Requests and responses are JSON. Create a token in
Settings → Connected Apps → API Tokens (read only, or read and change) and send it in
the Authorization header. A token is shown once; Todah stores only a fingerprint of it.
curl https://todah.ca/api/v1/invoices \
-H "Authorization: Bearer todah_pat_..."
curl -X POST https://todah.ca/api/v1/clients \
-H "Authorization: Bearer todah_pat_..." \
-H "Content-Type: application/json" \
-d '{"name": "Acme Ltd", "email": "ap@acme.example", "payment_terms": "Net 30"}'
GET /api/v1 lists the endpoints your token can call, and GET /api/v1/me
says whose workspace it is. Fields are the same as the Todah app's own: amounts are numbers in your
currency, tax_rate and discount are percentages, dates are
YYYY-MM-DD, and an invoice line is
{"description", "quantity", "unit_price", "tax_rate", "discount"}.
Rules
- Every request runs through the same checks as the app: your role, your plan's limits, and the rules on each record (for example, only a draft invoice can be edited).
- Nothing can be deleted through the API (
DELETEanswers 405), and bank data, refunds, payment links, settings, team and billing are not part of it. - Limits: 60 requests a minute per workspace, and
30 client emails an hour. Over a limit you get 429 with
Retry-After. - Errors are JSON:
{"error": "what went wrong and how to fix it"}, with 400 for a bad request, 401 for a missing or revoked token, 403 for a missing permission or scope, 404 for something that is not there or not in the API.
| Method | Path | Scope |
|---|---|---|
GET | /api/v1 | read |
GET | /api/v1/me | read |
GET | /api/v1/billable | read |
GET | /api/v1/billable/summary | read |
GET | /api/v1/bills | read |
POST | /api/v1/bills | write |
GET | /api/v1/bills/overdue | read |
GET | /api/v1/bills/{bill_id} | read |
PUT | /api/v1/bills/{bill_id} | write |
GET | /api/v1/clients | read |
POST | /api/v1/clients | write |
GET | /api/v1/clients/{cid} | read |
PUT | /api/v1/clients/{cid} | write |
GET | /api/v1/clients/{cid}/contacts | read |
POST | /api/v1/clients/{cid}/contacts | write |
PUT | /api/v1/clients/{cid}/contacts/{contact_id} | write |
GET | /api/v1/clients/{cid}/statement | read |
GET | /api/v1/credit-notes | read |
GET | /api/v1/credit-notes/{cn_id} | read |
GET | /api/v1/expense-categories | read |
GET | /api/v1/expenses/summary | read |
GET | /api/v1/invoices | read |
POST | /api/v1/invoices | write |
GET | /api/v1/invoices/next-number | read |
GET | /api/v1/invoices/overdue | read |
GET | /api/v1/invoices/{inv_id} | read |
PUT | /api/v1/invoices/{inv_id} | write |
GET | /api/v1/invoices/{inv_id}/payments | read |
POST | /api/v1/invoices/{inv_id}/payments | write |
POST | /api/v1/invoices/{inv_id}/send | write |
GET | /api/v1/products | read |
POST | /api/v1/products | write |
PUT | /api/v1/products/{pid} | write |
GET | /api/v1/quotes | read |
POST | /api/v1/quotes | write |
GET | /api/v1/quotes/next-number | read |
GET | /api/v1/quotes/{quote_id} | read |
PUT | /api/v1/quotes/{quote_id} | write |
POST | /api/v1/quotes/{quote_id}/convert | write |
POST | /api/v1/quotes/{quote_id}/send | write |
GET | /api/v1/recurring | read |
GET | /api/v1/recurring/{rid} | read |
GET | /api/v1/reports/ap-aging | read |
GET | /api/v1/reports/ar-aging | read |
GET | /api/v1/reports/cogs | read |
GET | /api/v1/reports/distributions | read |
GET | /api/v1/reports/expenses-by-vendor | read |
GET | /api/v1/reports/gst | read |
GET | /api/v1/reports/income-by-customer | read |
GET | /api/v1/reports/monthly | read |
GET | /api/v1/reports/opex | read |
GET | /api/v1/reports/pnl | read |
GET | /api/v1/reports/tax-summary | read |
GET | /api/v1/sales-taxes | read |
GET | /api/v1/stats | read |
GET | /api/v1/vendors | read |
POST | /api/v1/vendors | write |
PUT | /api/v1/vendors/{vendor_id} | write |
Building an integration with sign-in (OAuth)
Apps that act for many Todah customers should use OAuth 2.1 instead of asking people for a token.
Todah publishes its metadata at https://todah.ca/.well-known/oauth-authorization-server and
https://todah.ca/.well-known/oauth-protected-resource. It supports the authorisation code flow
with PKCE (S256), public clients, dynamic client registration, client ID metadata documents,
refresh-token rotation, revocation, and resource indicators (https://todah.ca/mcp or
https://todah.ca/api/v1). Scopes are todah.read and todah.write.
Privacy and your data
When you connect an AI app, what it reads from Todah goes to that app's provider, which may process it outside Canada under its own terms. Connect only apps you trust, and give read-only access where that is enough. See our Privacy Policy and Terms.
Support
Questions, problems or a security report: support@todah.ca.