Quento MCP API Documentation

Version: 1.0.0
Protocol: Model Context Protocol (MCP)
Base URL: https://{subdomain}.quento.app/mcp


Table of Contents

  1. Overview
  2. Getting Started
  3. Authentication
  4. Architecture
  5. API Tools Reference
  6. Request & Response Format
  7. Error Handling
  8. Best Practices
  9. Code Examples
  10. Troubleshooting

Overview

The Quento MCP (Model Context Protocol) API enables AI assistants and applications to interact with your invoicing system through a standardized protocol. Built on the MCP specification created by Anthropic, it allows natural language interactions with your business data.

What is MCP?

Model Context Protocol (MCP) is an open protocol that connects AI applications to external tools and data sources. It provides:

  • Standardized communication between AI models and business applications
  • Type-safe tool definitions with JSON Schema validation
  • Bidirectional streaming for real-time interactions
  • Multi-tool execution for complex workflows

Key Features

  • Natural Language Interface: Interact with invoices using plain language commands
  • Multi-Tenant Architecture: Automatic account isolation and data scoping
  • Auto-Generated CRUD Tools: Dynamically generated API tools from Rails models
  • Real-Time Updates: Server-Sent Events (SSE) support for streaming responses
  • Secure Authentication: API key-based authentication with Bearer token support
  • Type Safety: Full JSON Schema validation for all inputs and outputs

Use Cases

  • Invoice Management: Create, update, send, and track invoices via AI
  • Client & Product Management: CRUD operations for clients and products
  • Analytics & Reporting: Query statistics and generate insights
  • Email Automation: Send invoices and payment reminders
  • Multi-Step Workflows: Complex operations like "invoice all overdue clients"

Getting Started

Prerequisites

  1. Active Quento Account: Sign up at https://quento.app
  2. API Key: Generate from your account settings
  3. Subdomain: Your unique subdomain (e.g., acme.quento.app)

Quick Start

1. Get Your API Key

Navigate to Settings → Integrations → MCP API in your Quento account and click Generate New Key.

Your API key will look like: test_abc123def456...

Important: - Keep your API key secure - treat it like a password - Don't commit it to version control - Use environment variables to store it in your applications

2. Test the Connection

Using curl:

curl -X POST https://yoursubdomain.quento.app/mcp \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2024-11-05",
      "capabilities": {},
      "clientInfo": {"name": "test-client", "version": "1.0.0"}
    }
  }'

Expected Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "tools": {}
    },
    "serverInfo": {
      "name": "quento",
      "version": "1.0.0"
    }
  }
}

3. List Available Tools

curl -X POST https://yoursubdomain.quento.app/mcp \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list",
    "params": {}
  }'


Authentication

API Key Authentication

All requests require authentication using a Bearer token in the Authorization header.

Header Format:

Authorization: Bearer YOUR_API_KEY

Security Notes: - API keys are scoped to a single account - Keys have full read/write access to account data - Rotate keys regularly for security - Never commit keys to version control - Use environment variables to store keys

Account Scoping

The API automatically scopes all operations to the authenticated account based on:

  1. API Key: Identifies the account
  2. Subdomain: Verified against the API key's account
  3. Multi-Tenancy: All data is automatically isolated per account

You cannot access data from other accounts, even if you know their resource IDs. Every API request is scoped to your authenticated account.


Architecture

Communication Flow

┌─────────────────┐
│   AI Client     │  (Claude Desktop, ChatGPT, Custom App)
│  (MCP Client)   │
└────────┬────────┘
         │ 
         │ HTTPS POST Request
         │ Authorization: Bearer {API_KEY}
         │ Content-Type: application/json
         │
         ▼
┌─────────────────────────────────┐
│  https://{subdomain}.quento.app  │
│         POST /mcp               │
└────────┬────────────────────────┘
         │
         │ JSON-RPC 2.0 Request
         │ {
         │   "jsonrpc": "2.0",
         │   "method": "tools/call",
         │   "params": {...}
         │ }
         │
         ▼
┌─────────────────────────────────┐
│      MCP API Endpoint           │
│  - Authenticates API key        │
│  - Scopes to account            │
│  - Executes tool                │
│  - Returns JSON-RPC response    │
└────────┬────────────────────────┘
         │
         │ JSON-RPC 2.0 Response
         │ {
         │   "jsonrpc": "2.0",
         │   "result": {...}
         │ }
         │
         ▼
┌─────────────────┐
│   AI Client     │  Receives structured response
│  (MCP Client)   │
└─────────────────┘

Available Tools

The API provides CRUD operations for the following resources. Click each tool name to jump to detailed documentation:

📄 Invoice Management

👥 Client Management

📦 Product Catalog

🏦 Bank Accounts

🏢 Company Management

🇵🇱 KSeF (Polish e-Invoice System)

📊 Analytics & Reporting


Transport Protocol

HTTP Transport: - Endpoint: POST https://{subdomain}.quento.app/mcp - Protocol: JSON-RPC 2.0 over HTTPS - Authentication: Bearer token in Authorization header - Content-Type: application/json - Session Management: Stateful sessions maintained per account - Streaming: Server-Sent Events (SSE) support for real-time updates


API Tools Reference

Invoice Tools

create_invoice

Create a new invoice in draft status.

Input Schema:

{
  "client_id": 123,
  "company_id": 456,
  "items": [
    {
      "description": "Web Development",
      "quantity": 10,
      "unit_price": 100.00,
      "vat_rate": 23,
      "unit": "h",
      "product_id": 789
    }
  ],
  "payment_terms": 14,
  "sale_date": "2025-01-15",
  "issue_date": "2025-01-15",
  "payment_method": "transfer",
  "bank_account_id": 1,
  "currency": "EUR",
  "notes": "Notes visible on invoice PDF",
  "internal_notes": "Private notes (not on PDF)",
  "issue_location": "Warsaw",
  "purchase_order_number": "PO-2025-001",
  "split_payment": false,
  "pdf_secondary_language": "en",
  "discount_percentage": 0
}

Required Fields: client_id, company_id, items

Optional Fields: | Field | Type | Description | |-------|------|-------------| | payment_terms | integer | Days until due (default: 14) | | sale_date | string | Sale date in YYYY-MM-DD format | | issue_date | string | Issue date in YYYY-MM-DD format | | payment_method | string | transfer, cash, or card | | bank_account_id | integer | Bank account ID for transfer payments | | currency | string | Currency code (PLN, EUR, USD, GBP) | | notes | string | Notes visible on invoice PDF footer | | internal_notes | string | Private notes (not visible on PDF) | | issue_location | string | Place of issue (e.g., "Warsaw") | | purchase_order_number | string | Client's PO number | | split_payment | boolean | Enable Polish VAT split payment (MPP) | | pdf_secondary_language | string | Bilingual PDF: en, pl, or de (must differ from company's primary language) | | discount_percentage | number | Discount percentage (0-100) |

Response:

✓ Invoice created successfully!

Invoice Details:
- ID: 42
- Number: Not yet assigned (draft)
- Client: Acme Corp
- Total: 1,230.00 EUR
- Status: draft
- Due Date: 2025-01-29
- Items: 1

Items:
  • Web Development: 10 × 100.00 EUR (VAT: 23%)

Next Steps:
- Use GetInvoiceTool (id: 42) to view full details
- Use UpdateInvoiceTool (id: 42) to make changes
- Use ChangeInvoiceStatusTool (id: 42, action: 'issue') to issue the invoice
- Use SendInvoiceEmailTool (id: 42) to send to client


update_invoice

Update an existing invoice (must be in draft status).

Input Schema:

{
  "id": 42,
  "client_id": 123,
  "payment_terms": 30,
  "payment_method": "transfer",
  "bank_account_id": 1,
  "notes": "Updated notes (use empty string to clear)",
  "internal_notes": "Private notes",
  "issue_location": "Kraków",
  "purchase_order_number": "PO-2025-002",
  "split_payment": true,
  "pdf_secondary_language": "de",
  "discount_percentage": 10,
  "items": [
    {
      "description": "Updated service",
      "quantity": 5,
      "unit_price": 200.00,
      "vat_rate": 23
    }
  ]
}

Required Fields: id or invoice_number

Note: Providing items replaces all existing invoice items.

Response:

Invoice updated successfully! Invoice #INV/001/01/2025 for Acme Corp. 
Total: 1,230.00 EUR. Status: draft. Due date: 2025-01-29


list_invoices

List invoices with optional filtering.

Input Schema:

{
  "status": "issued",
  "client_id": 123,
  "currency": "EUR",
  "from_date": "2025-01-01",
  "to_date": "2025-01-31",
  "limit": 10
}

Status Options: draft, issued, sent, paid, overdue, cancelled

Response:

Found 3 invoice(s):

- Invoice #INV/001/01/2025 | Client: Acme Corp | Total: 1,230.00 EUR | Status: issued | Due: 2025-01-29 | Created: 2025-01-15
- Invoice #INV/002/01/2025 | Client: TechStart | Total: 2,500.00 EUR | Status: issued | Due: 2025-02-01 | Created: 2025-01-18
- Invoice #INV/003/01/2025 | Client: SmithCo | Total: 850.00 EUR | Status: paid | Due: 2025-01-25 | Created: 2025-01-11


get_invoice

Get detailed information about a specific invoice.

Input Schema:

{
  "id": 42
}

Response:

Invoice #42:

- Invoice number: INV/001/01/2025
- Client: Acme Corp
- Company: My Company Ltd
- Total cents: 123000
- Currency: EUR
- Aasm state: issued
- Sale date: 2025-01-15
- Issue date: 2025-01-15
- Due date: 2025-01-29
- Payment method: bank_transfer
- Payment terms: 14


mark_invoice_paid

Mark an invoice as paid.

Input Schema:

{
  "id": 42,
  "payment_date": "2025-01-20"
}

Alternative: Use invoice_number instead of id

Response:

Invoice #INV/001/01/2025 marked as paid successfully! 
Total: 1,230.00 EUR. Paid at: 2025-01-20


change_invoice_status

Change invoice status (issue, send, cancel).

Input Schema:

{
  "id": 42,
  "action": "issue"
}

Actions: issue, send_invoice, mark_paid, cancel

Response:

✓ Invoice status changed successfully!

Invoice: #INV/001/01/2025
Previous Status: draft
New Status: issued


send_invoice_email

Send invoice email to client (auto-issues draft invoices).

Input Schema:

{
  "id": 42,
  "recipient_email": "client@example.com",
  "custom_message": "Thank you for your business!"
}

Response:

✓ Invoice sent successfully!

✓ Invoice was automatically issued before sending

Email Details:
- Recipient: client@example.com
- Invoice: #INV/001/01/2025
- Total: 1,230.00 EUR
- Due Date: 2025-01-29
- Status: sent

✓ Custom message included in email

Next Steps:
- Use GetInvoicePdfLinkTool (id: 42) to get preview link
- Use MarkInvoicePaidTool (id: 42) when payment is received


get_invoice_pdf_link

Get a link to view/download the invoice PDF.

Input Schema:

{
  "id": 42
}

Response:

Invoice PDF Link: https://acme.quento.app/invoices/42.pdf

Note: Invoice must be issued or later to generate PDF


Client Tools

create_client

Create a new client.

Input Schema:

{
  "name": "Acme Corporation",
  "email": "contact@acme.com",
  "tax_id": "1234567890",
  "country": "PL",
  "street": "ul. Główna 1",
  "city": "Warsaw",
  "postal_code": "00-001",
  "currency": "EUR",
  "default_payment_terms": 30
}

Required Fields: name, country

Response:

Client created successfully! ID: 123


update_client

Update an existing client.

Input Schema:

{
  "id": 123,
  "email": "newemail@acme.com",
  "default_payment_terms": 45
}

Required Fields: id

Response:

Client updated successfully! ID: 123


get_client

Get client details.

Input Schema:

{
  "id": 123
}

Response:

Client #123:

- Name: Acme Corporation
- Email: contact@acme.com
- Tax id: 1234567890
- Street: ul. Główna 1
- City: Warsaw
- Postal code: 00-001
- Country: PL
- Currency: EUR
- Default payment terms: 30


list_clients

List all clients with optional search.

Input Schema:

{
  "search": "acme",
  "limit": 50,
  "offset": 0
}

Response:

Found 1 client(s):

Client: Acme Corporation
  Email: contact@acme.com
  Tax ID: 1234567890
  Address: ul. Główna 1, Warsaw, 00-001, PL
  Currency: EUR | Payment Terms: 30 days
  ID: 123


Product Tools

create_product

Create a new product or service.

Input Schema:

{
  "name": "Web Development",
  "unit_price": 150.00,
  "vat_rate": "23",
  "unit": "h",
  "sku": "WEB-DEV-01",
  "description": "Hourly web development services",
  "currency": "EUR"
}

Required Fields: name, unit_price

VAT Rates: 23, 8, 5, 0, zw. (exempt), np. (not applicable)

Units: pcs, kg, g, l, ml, m, m2, m3, h, day, month, year

Response:

Product created successfully!

- Name: Web Development
- Price: 150.00 EUR per h
- VAT: 23%
- SKU: WEB-DEV-01
- ID: 1


update_product

Update an existing product.

Input Schema:

{
  "id": 1,
  "name": "Web Development (Premium)",
  "unit_price": 200.00,
  "active": true
}

Required Fields: id

Response:

Product updated successfully!

- Name: Web Development (Premium)
- Price: 200.00 EUR per h
- VAT: 23%
- SKU: WEB-DEV-01
- Active: Yes
- ID: 1


get_product

Get product details.

Input Schema:

{
  "id": 1
}

Response:

Product #1:

- Name: Web Development
- Price: 150.00 EUR per h
- VAT: 23%
- SKU: WEB-DEV-01
- Description: Hourly web development services
- Active: Yes
- Used on invoices: 12 time(s)
- Created: 2025-01-15


list_products

List all products/services.

Input Schema:

{
  "search": "development",
  "limit": 50
}

Response:

Found 2 products:

- Software Development (Hourly) | Price: 150.00 EUR | VAT: 23% | ID: 1
- Web Development (Project) | Price: 5000.00 EUR | VAT: 23% | ID: 2


Bank Account Tools

create_bank_account

Create a new bank account for a company. Bank accounts are used on invoices for payment instructions.

Input Schema:

{
  "company_id": 1,
  "bank_name": "mBank",
  "account_number": "12345678901234567890123456",
  "currency": "PLN",
  "iban": "PL61109010140000071219812874",
  "swift_code": "BREXPLPW",
  "default": true,
  "active": true
}

Required Fields: company_id, bank_name, account_number, currency

Currencies: PLN, EUR, USD, GBP

Response:

Bank account created successfully!

ID: 1
Company: My Company Ltd
Bank: mBank
Account: 12345678901234567890123456
Currency: PLN
IBAN: PL61109010140000071219812874
SWIFT: BREXPLPW
Default: Yes
Status: Active


update_bank_account

Update an existing bank account. Changes do NOT affect already-issued invoices.

Input Schema:

{
  "id": 1,
  "bank_name": "mBank S.A.",
  "default": true,
  "active": true
}

Required Fields: id

Response:

Bank account updated successfully!

ID: 1
Company: My Company Ltd
Bank: mBank S.A.
Account: 12345678901234567890123456
Currency: PLN
Default: Yes
Status: Active

Note: Already-issued invoices retain their original bank details.


list_bank_accounts

List bank accounts for a company.

Input Schema:

{
  "company_id": 1,
  "currency": "PLN",
  "active_only": true
}

Response:

Bank Accounts:

## My Company Ltd
  - ID: 1 | mBank | 12345678901234567890123456 | PLN [DEFAULT]
    IBAN: PL61109010140000071219812874
    SWIFT: BREXPLPW
  - ID: 2 | Santander | 98765432109876543210987654 | EUR
    IBAN: PL27114020040000300201355387


Company Tools

list_companies

List companies (sellers) in your account.

Input Schema:

{
  "search": "acme",
  "limit": 10
}

Response:

Found 2 company(ies):

- Acme Corp | Tax ID: 5260250274 | Currency: PLN | ID: 1
- Acme International | Tax ID: DE123456789 | Currency: EUR | ID: 2


create_company

Create a new company (seller) for issuing invoices.

Input Schema:

{
  "name": "LUX MED Sp. z o.o.",
  "country": "PL",
  "currency": "PLN",
  "locale": "pl",
  "nip": "5272523421",
  "regon": "017370029",
  "street": "ul. Postępu 21C",
  "city": "Warsaw",
  "postal_code": "02-676",
  "email": "kontakt@luxmed.pl",
  "phone": "+48 22 332 28 00",
  "website": "https://luxmed.pl",
  "default_payment_terms": 14,
  "invoice_number_pattern": "FV/{num}/{month}/{year}",
  "default": false,
  "active": true
}

Required Fields: name, country, currency

Country Codes: PL, DE, US, GB, FR, IT, ES, NL, BE, AT, CH

Tax IDs: - For Polish companies: use nip (10 digits) and optionally regon - For other countries: use tax_id

Response:

Company created successfully!

ID: 1
Name: LUX MED Sp. z o.o.
Country: PL
Currency: PLN
Locale: pl
NIP: 5272523421
REGON: 017370029
Address: ul. Postępu 21C, Warsaw, 02-676, PL
Email: kontakt@luxmed.pl
Phone: +48 22 332 28 00
Website: https://luxmed.pl
Payment terms: 14 days
Invoice pattern: FV/{num}/{month}/{year}
Status: Active

Next steps: Use create_bank_account to add a bank account for this company.


update_company

Update an existing company's details.

Input Schema:

{
  "id": 1,
  "name": "LUX MED Sp. z o.o.",
  "email": "nowy-email@luxmed.pl",
  "default_payment_terms": 30,
  "default": true
}

Required Fields: id

Note: Use empty string "" to clear optional text fields.

Response:

Company updated successfully!

ID: 1
Name: LUX MED Sp. z o.o.
Country: PL
Currency: PLN
Locale: pl
NIP: 5272523421
Address: ul. Postępu 21C, Warsaw, 02-676, PL
Email: nowy-email@luxmed.pl
Payment terms: 30 days
Default company: Yes
Status: Active


get_company

Get detailed information about a company.

Input Schema:

{
  "id": 1
}

Alternative: Use name (partial match) or nip instead of id

Response:

=== LUX MED Sp. z o.o. ===

Basic Information:
  ID: 1
  Country: PL
  Currency: PLN
  Locale: pl
  Status: Active
  Default: Yes

Tax Information:
  NIP: 5272523421
  REGON: 017370029

Address:
  Street: ul. Postępu 21C
  City: Warsaw
  Postal Code: 02-676

Contact:
  Email: kontakt@luxmed.pl
  Phone: +48 22 332 28 00
  Website: https://luxmed.pl

Invoice Settings:
  Payment Terms: 14 days
  Number Pattern: FV/{num}/{month}/{year}

Bank Accounts:
  - mBank: 12345678901234567890123456 (PLN) (default) [ID: 1]

KSeF (Polish e-Invoice):
  Status: Enabled (token)
  Environment: production


lookup_company

Look up company data from the Polish VAT Registry using NIP (tax ID). Returns company name, address, VAT status, REGON, KRS, registered bank accounts, email, and website. Use this to auto-fill client data.

Input Schema:

{
  "nip": "5260250274",
  "date": "2025-01-15"
}

Required Fields: nip

NIP Format: Can include dashes (e.g., "526-025-02-74") or just digits

Response:

# Company Found

**Name:** Example Company Sp. z o.o.
**NIP:** 526-025-02-74
**VAT Status:** Active (Czynny) ✓
**REGON:** 012345678
**KRS:** 0000123456

## Address
**Street:** ul. Przykładowa 1
**City:** Warsaw
**Postal Code:** 00-001
**Country:** PL

## Contact
**Email:** contact@example.com
**Website:** https://example.com

## Registered Bank Accounts
- PL 61 1090 1014 0000 0712 1981 2874
- PL 27 1140 2004 0000 3002 0135 5387

---
_Data from Polish VAT Registry (date: 2025-01-15)_


KSeF Tools (Polish e-Invoice System)

KSeF (Krajowy System e-Faktur) is Poland's national e-invoicing system. These tools allow you to submit invoices to KSeF and track their status.

Prerequisites: - Company must be Polish (country: PL) - Company must have KSeF configured with a valid authorization token - Invoice must be issued before submitting to KSeF

submit_invoice_to_ksef

Submit an invoice to Poland's KSeF system.

Input Schema:

{
  "id": 42
}

Alternative: Use invoice_number instead of id

Response:

KSeF submission initiated successfully!

Submission Details:
- Submission ID: 1
- Invoice: INV/001/01/2025
- Company: My Company Ltd (NIP: 5260250274)
- Environment: production
- Status: pending

The submission is being processed in the background. This typically takes 10-60 seconds.

Next Steps:
- Use GetKsefStatusTool (invoice_id: 42) to check submission status
- Once accepted, use GetKsefUpoTool (invoice_id: 42) to get the official confirmation (UPO)


get_ksef_status

Check the KSeF submission status for an invoice.

Input Schema:

{
  "invoice_id": 42
}

Alternative: Use invoice_number or submission_id

Response:

KSeF Status for Invoice INV/001/01/2025
==================================================

Submission #1 (ID: 1)
------------------------------
Status: ACCEPTED
Created: 2025-01-15 10:30:00
Submitted: 2025-01-15 10:30:15
KSeF Reference: 1234567890-20250115-ABC123DEF456

Next Steps:
- Use GetKsefUpoTool to get the official confirmation (UPO)
- The KSeF reference number can be used for official correspondence

Status Values: | Status | Description | |--------|-------------| | pending | Waiting to be processed | | submitted | Sent to KSeF, awaiting response | | accepted | Successfully accepted by KSeF | | rejected | Rejected by KSeF (check error message) | | error | Technical error (can retry) |


list_ksef_submissions

List recent KSeF submissions with their statuses.

Input Schema:

{
  "status": "accepted",
  "company_id": 1,
  "limit": 20
}

Status Filter: pending, submitted, accepted, rejected, error

Response:

KSeF Submissions
============================================================

Summary: 25 total submissions
  - pending: 1
  - submitted: 2
  - accepted: 20
  - rejected: 1
  - error: 1

Recent Submissions (showing 20):
------------------------------------------------------------

[OK]  #20 | Invoice: INV/020/01/2025 | Company: My Company Ltd | 2025-01-20 14:30
       KSeF Ref: 1234567890-20250120-XYZ789

[OK]  #19 | Invoice: INV/019/01/2025 | Company: My Company Ltd | 2025-01-19 11:15
       KSeF Ref: 1234567890-20250119-DEF456

[X]   #18 | Invoice: INV/018/01/2025 | Company: My Company Ltd | 2025-01-18 09:00
       Error: Invalid VAT rate for item 2

Use GetKsefStatusTool (submission_id: ID) for detailed information about a specific submission.


Statistics Tools

get_statistics

Get invoice statistics and analytics.

Input Schema:

{
  "period": "current_month",
  "currency": "EUR"
}

Period Options: current_month, last_month, current_year, last_year, all_time

Response:

Invoice Statistics (Current month) (EUR only):

Overview:
- Total Invoices: 15
- Draft: 2
- Issued: 5
- Sent: 3
- Paid: 4
- Overdue: 1
- Cancelled: 0

Revenue:
- Total Revenue (Paid): 12,500.00 EUR
- Outstanding Amount: 8,300.00 EUR

Top 5 Clients by Revenue:
  - Acme Corp: 5,000.00 EUR
  - TechStart: 3,500.00 EUR
  - SmithCo: 2,000.00 EUR


Request & Response Format

JSON-RPC 2.0 Protocol

All MCP communication uses JSON-RPC 2.0 over HTTP.

Request Structure:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_invoice",
    "arguments": {
      "client_id": 123,
      "company_id": 456,
      "items": [...]
    }
  }
}

Success Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Invoice created successfully! ID: 42"
      }
    ]
  }
}

Error Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32600,
    "message": "Invalid Request",
    "data": {
      "details": "Missing required parameter: client_id"
    }
  }
}

MCP Tool Response Format

All tools return responses in a consistent format with an array of content objects:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Human-readable result message"
      }
    ]
  }
}

Future Support (not yet implemented): - type: "image" - Embedded images - type: "resource" - File attachments


Error Handling

Common Error Codes

CodeMeaningExample
-32700Parse errorInvalid JSON
-32600Invalid RequestMissing jsonrpc field
-32601Method not foundUnknown tool name
-32602Invalid paramsMissing required field
-32603Internal errorDatabase error
401UnauthorizedMissing/invalid API key
403ForbiddenAPI key doesn't match subdomain
404Not FoundResource doesn't exist
422UnprocessableValidation failed

Tool-Level Errors

Tools return user-friendly error messages in the response text:

Example - Client Not Found:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Client not found"
      }
    ]
  }
}

Example - Validation Error:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Failed to create invoice: Client can't be blank, Items can't be empty"
      }
    ]
  }
}

Error Handling Best Practices

1. Check HTTP Status First:

if (response.status === 401) {
  console.error("Authentication failed - check API key");
}

2. Parse JSON-RPC Error:

const data = await response.json();
if (data.error) {
  console.error(`MCP Error: ${data.error.message}`);
}

3. Handle Tool Errors:

const text = data.result.content[0].text;
if (text.includes("not found") || text.includes("Failed")) {
  console.error(`Tool Error: ${text}`);
}


Best Practices

Security

1. API Key Management:

# ✅ Good: Use environment variables
export QUENTO_API_KEY="test_abc123..."

# ❌ Bad: Hardcode in source code
api_key = "test_abc123..."  # NEVER DO THIS

2. Rotate Keys Regularly:

Rotate your API keys every 90 days for enhanced security. You can generate a new key from Settings → Integrations → MCP API.

Performance

1. Use Pagination:

{
  "method": "tools/call",
  "params": {
    "name": "list_invoices",
    "arguments": {
      "limit": 20,
      "offset": 0
    }
  }
}

2. Filter Server-Side:

//  Good: Filter in database
{"status": "paid", "currency": "EUR"}

//  Bad: Fetch all and filter client-side
{"limit": 1000}

3. Cache Tool List:

// Cache tools/list response for 1 hour
const tools = await fetchAndCache('/mcp', 'tools/list', 3600);

Data Integrity

1. Validate Before Sending:

// ✅ Good: Validate required fields
if (!client_id || !items.length) {
  throw new Error("Missing required fields");
}

2. Handle Partial Failures:

// When creating multiple invoices, handle each error separately
const results = await Promise.allSettled(
  invoices.map(inv => createInvoice(inv))
);


Code Examples

JavaScript/Node.js

Initialize MCP Client:

const fetch = require('node-fetch');

class QuentoMCP {
  constructor(subdomain, apiKey) {
    this.baseUrl = `https://${subdomain}.quento.app/mcp`;
    this.apiKey = apiKey;
    this.requestId = 1;
  }

  async call(method, params = {}) {
    const response = await fetch(this.baseUrl, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: this.requestId++,
        method,
        params
      })
    });

    const data = await response.json();

    if (data.error) {
      throw new Error(data.error.message);
    }

    return data.result;
  }

  async initialize() {
    return this.call('initialize', {
      protocolVersion: '2024-11-05',
      capabilities: {},
      clientInfo: { name: 'quento-js-client', version: '1.0.0' }
    });
  }

  async listTools() {
    return this.call('tools/list');
  }

  async callTool(name, arguments) {
    const result = await this.call('tools/call', { name, arguments });
    return result.content[0].text;
  }
}

// Usage
const client = new QuentoMCP('acme', process.env.QUENTO_API_KEY);
await client.initialize();

// Create invoice
const result = await client.callTool('create_invoice', {
  client_id: 123,
  company_id: 456,
  items: [
    {
      description: 'Web Development',
      quantity: 10,
      unit_price: 100,
      vat_rate: 23,
      unit: 'h'
    }
  ]
});

console.log(result);


Python

MCP Client with Ollama Integration:

import requests
import json

class QuentoMCP:
    def __init__(self, subdomain, api_key):
        self.base_url = f"https://{subdomain}.quento.app/mcp"
        self.api_key = api_key
        self.request_id = 1

    def call(self, method, params=None):
        response = requests.post(
            self.base_url,
            headers={
                'Authorization': f'Bearer {self.api_key}',
                'Content-Type': 'application/json'
            },
            json={
                'jsonrpc': '2.0',
                'id': self.request_id,
                'method': method,
                'params': params or {}
            }
        )

        self.request_id += 1
        data = response.json()

        if 'error' in data:
            raise Exception(data['error']['message'])

        return data['result']

    def initialize(self):
        return self.call('initialize', {
            'protocolVersion': '2024-11-05',
            'capabilities': {},
            'clientInfo': {'name': 'quento-py-client', 'version': '1.0.0'}
        })

    def list_tools(self):
        return self.call('tools/list')

    def call_tool(self, name, arguments):
        result = self.call('tools/call', {'name': name, 'arguments': arguments})
        return result['content'][0]['text']

# Usage
client = QuentoMCP('acme', 'test_abc123...')
client.initialize()

# List invoices
result = client.call_tool('list_invoices', {
    'status': 'paid',
    'limit': 10
})

print(result)


cURL

List Available Tools:

curl -X POST https://acme.quento.app/mcp \
  -H "Authorization: Bearer test_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {}
  }'

Create Invoice:

curl -X POST https://acme.quento.app/mcp \
  -H "Authorization: Bearer test_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "create_invoice",
      "arguments": {
        "client_id": 123,
        "company_id": 456,
        "items": [
          {
            "description": "Web Development",
            "quantity": 10,
            "unit_price": 100,
            "vat_rate": 23,
            "unit": "h"
          }
        ]
      }
    }
  }'


Troubleshooting

Common Issues

Issue: "Invalid API key"

Cause: API key is incorrect or doesn't match the subdomain

Solution: - Verify you're using the correct API key from your account settings - Check that the subdomain in the URL matches your account - Ensure the Authorization header is properly formatted: Bearer YOUR_API_KEY - Regenerate your API key if necessary from Settings → Integrations → MCP API


Issue: "Client not found"

Cause: Client ID doesn't exist or belongs to different account

Solution:

# List all clients
curl -X POST https://yoursubdomain.quento.app/mcp \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "list_clients",
      "arguments": {}
    }
  }'


Issue: "Failed to create invoice: validation errors"

Cause: Missing required fields or invalid data

Solution: Check the error message for specific validation failures: - Client can't be blank - Missing client_id - Items can't be empty - Missing items array - VAT rate is invalid - Check VAT rate format (use number, not string)

Example Fix:

//  Bad
{
  "vat_rate": "23%"
}

//  Good
{
  "vat_rate": 23
}


Issue: "Method not found"

Cause: Tool name is incorrect

Solution: List all available tools:

curl -X POST https://yoursubdomain.quento.app/mcp \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {}
  }'


Issue: "Invoice cannot be marked as paid"

Cause: Invoice is in draft or cancelled status

Solution: Issue the invoice first:

{
  "name": "change_invoice_status",
  "arguments": {
    "id": 42,
    "action": "issue"
  }
}

Then mark as paid:

{
  "name": "mark_invoice_paid",
  "arguments": {
    "id": 42
  }
}


Rate Limits

Current Limits: - 100 requests/minute per API key - 1000 requests/hour per account

Rate Limit Response:

{
  "error": {
    "code": 429,
    "message": "Rate limit exceeded. Try again in 60 seconds."
  }
}

Best Practice: Implement exponential backoff:

async function callWithRetry(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      if (error.code === 429 && i < maxRetries - 1) {
        await sleep(Math.pow(2, i) * 1000);
        continue;
      }
      throw error;
    }
  }
}


Resources

Official Documentation

Community & Support


Changelog

Version 1.2.0 (Current)

Released: 2025-12-05

New Features: - ✅ Company Management: Full CRUD for companies/sellers (create, update, get, list) - Create companies with Polish NIP/REGON or international tax IDs - Configure invoice number patterns, payment terms, locale - Automatic NIP validation for Polish companies

Security Improvements: - ❌ Removed all delete_* actions from MCP API for safety - Deletions must be performed through the web UI - Use update_* with active: false to deactivate records instead - ❌ Removed create_company and update_company tools - Company management is handled through the web UI during account setup - Prevents accidental data corruption or duplicate companies

Tool Count: 26/30 (4 slots reserved for future use)

Version 1.1.0

Released: 2025-12-04

New Features: - ✅ Bank Account Management: Full CRUD for bank accounts (create, update, get, delete, list) - ✅ KSeF Integration: Submit invoices to Poland's e-invoice system (submit, status, UPO, list) - ✅ Product Management: Full CRUD for products (create, update, get, delete) - ✅ Company Lookup: Look up Polish companies by NIP from VAT Registry - ✅ Client Deletion: Delete clients (with invoice protection)

Enhanced Invoice Tools: - ✅ bank_account_id - Assign bank account for transfer payments - ✅ internal_notes - Private notes not visible on PDF - ✅ issue_location - Place of issue for invoices - ✅ purchase_order_number - Client's PO number reference - ✅ split_payment - Polish VAT split payment (MPP) support - ✅ pdf_secondary_language - Bilingual PDF support (en/pl/de)

Security Improvements: - ✅ Safe date parsing (ISO 8601 only) - ✅ Error message sanitization - ✅ Input filtering for CRUD operations

Version 1.0.0

Released: 2025-01-28

Features: - ✅ Full invoice CRUD operations - ✅ Client management (create, update, list, get) - ✅ Product listing - ✅ Statistics and analytics - ✅ Email automation - ✅ PDF generation - ✅ Multi-tenant account scoping - ✅ JSON-RPC 2.0 over HTTPS


License

Quento MCP API is proprietary software.
© 2025 Quento. All rights reserved.

For licensing inquiries, contact: support@quento.app


Last Updated: December 5, 2025
API Version: 1.0.0
Protocol Version: 2024-11-05