Connect Pennylane to ChatGPT: Sync Ledger and Financial Reporting
Learn how to connect Pennylane to ChatGPT using a managed MCP server. Automate ledger lettering, FEC exports, and invoice processing with AI.
If you are engineering an AI workflow to audit ledgers, generate FEC reports, or process e-invoices, you need to connect Pennylane to ChatGPT using a Model Context Protocol (MCP) server. This server acts as the translation layer between the LLM's natural language tool calls and the highly structured, state-dependent Pennylane REST API.
If your team uses Claude, check out our guide on connecting Pennylane to Claude or explore our broader architectural overview on connecting Pennylane to AI Agents.
Giving a Large Language Model (LLM) read and write access to a core accounting system is inherently high-stakes. You are dealing with immutable states (like finalized invoices) and strict financial compliance structures. You can either spend engineering cycles building, hosting, and maintaining a custom MCP server, or you can use a managed infrastructure layer to handle the dynamic schema generation, token lifecycle, and secure tool routing. This guide breaks down exactly how to use Truto to generate a secure MCP server for Pennylane, connect it natively to ChatGPT, and execute complex accounting workflows using AI.
The Engineering Reality of the Pennylane API
A custom MCP server is a self-hosted backend that listens for JSON-RPC 2.0 messages from an LLM and maps them to HTTP requests against third-party endpoints. While the MCP spec provides a standardized way for models to discover tools, it does not abstract away the underlying API's domain logic.
When you build an integration against Pennylane, you are not just executing basic CRUD operations. You are interacting with an accounting engine that enforces strict European financial compliance rules. Here are the specific engineering constraints you must account for when exposing Pennylane to an AI agent:
Asynchronous Export Polling
When an LLM needs to audit the General Ledger or generate a Fichier d'Ecritures Comptables (FEC), it cannot fetch the data synchronously. Creating an export like create_a_pennylane_exports_fec simply schedules a background job and returns an id with a processing status. To retrieve the file, the agent must be instructed to pause, wait, and repeatedly call get_single_pennylane_exports_fec_by_id until the status changes to ready. Furthermore, the returned file_url is a time-limited AWS S3 link that expires exactly 30 minutes after generation. Your agent must process the payload immediately.
Complex Ledger Lettering Logic
"Lettering" (lettrage) is the process of reconciling invoice lines with payment lines in French accounting. The Pennylane API does not simply let you link two IDs together. Calling the lettering endpoint requires passing an array of ledger_entry_lines (minimum 2) and an explicit unbalanced_lettering_strategy. If an AI agent attempts to letter a line that is already part of an existing sequence, Pennylane merges the new lines into the existing lettering group rather than failing. The LLM must be explicitly prompted on how to handle these merging semantics.
E-Invoicing and Factur-X Strict Mappings
Importing a customer e-invoice (create_a_pennylane_e_invoices_import) via a Factur-X PDF is a highly rigid process. If you provide optional invoice_options to pre-fill line-level data, the e_invoice_line_id mapped by the agent must perfectly match the LineID defined in the Factur-X BT-126 specification embedded within the PDF. A mismatch results in an immediate 422 Unprocessable Entity error.
Rate Limits and 429 Behaviors
Pennylane enforces strict API quotas. Factual note on rate limits: Truto does not retry, throttle, or apply exponential backoff on rate limit errors. When the upstream Pennylane API returns an HTTP 429, Truto passes that error directly to the caller. Truto normalizes the upstream rate limit information into standardized headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset) per the IETF specification. The caller (the AI agent framework) is completely responsible for handling the 429, reading the reset header, and scheduling the retry.
Generating the Managed MCP Server
Rather than hand-coding tool schemas for every Pennylane endpoint, Truto dynamically derives MCP tools directly from the integration's resource definitions and documentation records.
Every MCP server is backed by a cryptographic token scoped to a single integrated account. This token serves as both the routing instruction and the authentication mechanism. You can generate this server via the Truto UI or programmatically via the API.
Option 1: Via the Truto UI
- Navigate to the integrated account page for your Pennylane connection.
- Click the MCP Servers tab.
- Click Create MCP Server.
- Select your desired configuration (e.g., restrict to
readoperations or specific tool tags). - Copy the generated MCP server URL (e.g.,
https://api.truto.one/mcp/a1b2c3d4e5f6...).
Option 2: Via the API
You can dynamically provision MCP servers for your end-users using the Truto REST API. Truto validates the configuration, hashes the token securely via a signing key, provisions Cloudflare KV storage, and schedules an expiration alarm if a TTL is provided.
// POST /integrated-account/:id/mcp
const response = await fetch("https://api.truto.one/integrated-account/acc_12345/mcp", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_TRUTO_API_TOKEN",
"Content-Type": "application/json"
},
body: JSON.stringify({
name: "Pennylane Month-End Auditing Agent",
config: {
methods: ["read", "write"], // Allow reading ledgers and creating exports
tags: ["finance", "ledger"]
},
expires_at: "2026-12-31T23:59:59Z"
})
});
const mcpServer = await response.json();
console.log(mcpServer.url);
// Output: https://api.truto.one/mcp/a1b2c3d4e5f6...When a tool is invoked over this endpoint via JSON-RPC, Truto splits the single flat argument object passed by the LLM into query parameters and body parameters using the underlying API's JSON schemas. It then proxies the request to Pennylane, handling the OAuth refresh lifecycle automatically.
Connecting the MCP Server to ChatGPT
Once you have the secure URL, connecting it to an AI agent requires zero coding on the client side. The URL contains everything needed to authenticate and discover the dynamically generated tools.
Option A: Via the ChatGPT UI
If you are using ChatGPT Enterprise, Pro, or Team with Developer Mode enabled:
- In ChatGPT, go to Settings -> Apps -> Advanced settings.
- Toggle Developer mode to ON.
- Under MCP servers / Custom connectors, click Add a new server.
- Name the server (e.g., "Pennylane Financials").
- Paste the Truto MCP URL into the Server URL field.
- Click Save. ChatGPT will immediately send an
initializerequest to discover the Pennylane tools.
Option B: Via Manual Config File (CLI / Desktop Clients)
For frameworks and desktop clients that rely on standard MCP configuration files, you configure the server using the SSE (Server-Sent Events) transport wrapper.
{
"mcpServers": {
"pennylane_financials": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-sse",
"https://api.truto.one/mcp/a1b2c3d4e5f6..."
]
}
}
}Security and Access Control
Exposing an accounting API to an LLM requires strict guardrails. Truto's MCP architecture provides several layers of access control, enforced at the token level before a tool is ever generated:
- Method Filtering: By passing
config: { methods: ["read"] }during creation, the server will strictly blockcreate,update, anddeleteoperations. The LLM won't even see write tools in itstools/listresponse. - Tag Filtering: You can scope the agent's context by supplying tags like
config: { tags: ["invoices"] }. If a Pennylane resource isn't tagged with "invoices" in the Truto integration config, it is completely excluded. - Require API Token Auth: Setting
require_api_token_auth: trueforces a dual-layer authentication check. The client must possess both the hashed token in the URL and a valid Truto APIBearertoken in theAuthorizationheader. This prevents leaked URLs from being exploited. - Automated Expiration: If an
expires_atvalue is set, Truto utilizes a Cloudflare Durable Object alarm to aggressively prune the token from the database and KV store at the exact timestamp. Stale access is impossible.
Hero Tools for Pennylane
Truto dynamically generates specific, highly contextual tools from the Pennylane integration. Here are the highest-leverage operations your AI agent can perform, complete with example prompts.
list_all_pennylane_external_ledger_entry_lines
Retrieves ledger entry lines, serving as the foundational read tool for auditing. It returns debit, credit, date, ledger account mapping, and lettering status. This supports extensive filtering by journal ID, account ID, and date ranges.
"Fetch all ledger entry lines for account ID 1045 from the past 30 days. Identify any lines that are currently unlettered and sum their total debit values."
create_a_pennylane_ledger_entry_lines_lettering
Executes the lettering logic to reconcile multiple ledger entries. You must pass a minimum of two ledger_entry_lines and define the unbalanced_lettering_strategy.
"Letter the ledger entry line ID 84729 with line ID 84730. If they do not balance perfectly, apply the 'create_profit_and_loss_entry' unbalanced lettering strategy."
create_a_pennylane_exports_fec
Initiates the asynchronous generation of a Fichier d'Ecritures Comptables (FEC). This tool returns a job ID and a processing status, requiring the agent to follow up.
"Generate an FEC export for the period starting January 1st to January 31st. Give me the export ID so we can poll for the download link."
get_single_pennylane_exports_fec_by_id
Polls the status of an existing FEC export job. Once the backend processing is complete, this tool returns the temporary S3 download link.
"Check the status of FEC export ID 99281. If the status is 'ready', extract the file URL so I can securely download the archive."
update_a_pennylane_customer_invoice_finalize_by_id
Transitions a draft customer invoice into an immutable, finalized state. Once called, the invoice receives a permanent invoice number and can no longer be edited.
"Review draft invoice ID 5510. Ensure the line items match the quote, and if the total currency amount is correct, finalize the invoice to lock its state."
create_a_pennylane_external_billing_subscription
Automates recurring revenue by creating a billing subscription. Pennylane will handle the recurring invoice generation based on the rule and can optionally trigger GoCardless debits.
"Create a monthly billing subscription for customer ID 1092 starting on the 1st of next month. Ensure the recurring rule is set to trigger invoice generation automatically."
To view the complete schema definitions, query parameters, and the full inventory of tools available, review the Pennylane integration page.
Workflows in Action
AI agents excel at orchestrating multi-step workflows that require conditional logic based on API responses. Here is how two distinct personas utilize the Pennylane MCP server.
Persona 1: The Staff Accountant performing Month-End FEC Audits
During month-end close, an accountant needs to verify that the general ledger conforms to French FEC formatting before passing it to auditors. The agent must trigger an async job, poll for completion, and analyze the resulting dataset.
"Generate an FEC export for the previous month. Wait for the export to process, then get the download URL. Once you have it, summarize the total volume of ledger entries processed."
Tool Calling Sequence:
create_a_pennylane_exports_fec: The LLM submits the date range and receivesid: "exp_987"withstatus: "processing".get_single_pennylane_exports_fec_by_id: The LLM polls the endpoint using the ID. If it receivesstatus: "processing", it waits.get_single_pennylane_exports_fec_by_id: The LLM polls again, receivingstatus: "ready"and afile_url.
sequenceDiagram
participant ChatGPT as ChatGPT
participant TrutoMCP as Truto MCP Server
participant Pennylane as Pennylane API
ChatGPT->>TrutoMCP: call create_a_pennylane_exports_fec
TrutoMCP->>Pennylane: POST /exports/fec
Pennylane-->>TrutoMCP: 201 Created (id: exp_987, status: processing)
TrutoMCP-->>ChatGPT: JSON-RPC Result
loop Polling
ChatGPT->>TrutoMCP: call get_single_pennylane_exports_fec_by_id<br>(id: exp_987)
TrutoMCP->>Pennylane: GET /exports/fec/exp_987
Pennylane-->>TrutoMCP: 200 OK (status: ready, file_url: https://...)
TrutoMCP-->>ChatGPT: JSON-RPC Result
endResult: The accountant receives the secure, time-limited download URL immediately upon completion, bypassing the need to manually navigate the Pennylane UI and refresh the exports tab.
Persona 2: Accounts Receivable processing Invoice Matching & Finalization
An AR specialist uses the agent to verify transactions against draft invoices, lock the invoice, and dispatch the final PDF to the client.
"Find the draft invoice for customer ID 405. Check if there are any matched transactions. If the outstanding balance is covered, finalize the invoice and send it to the customer by email."
Tool Calling Sequence:
list_all_pennylane_customer_invoice_matched_transactions: The LLM queries the matched banking transactions attached to the draft invoice.update_a_pennylane_customer_invoice_finalize_by_id: Seeing that the transaction covers the amount, the LLM finalizes the draft, making it immutable and generating the official invoice number.create_a_pennylane_customer_invoice_send_by_email: The LLM triggers the email dispatch. (Note: The LLM must be instructed to handle a 409 conflict gracefully if the PDF generation is delayed, implementing backoff before retrying).
flowchart TD
A["list_all_pennylane_customer_invoice_<br>matched_transactions"] --> B{"Is balance covered?"}
B -- Yes --> C["update_a_pennylane_customer_<br>invoice_finalize_by_id"]
B -- No --> D["Flag for manual review"]
C --> E["create_a_pennylane_customer_<br>invoice_send_by_email"]Result: The AR workflow transitions from a multi-click, manual verification process into a single natural language command. The agent handles the state verification, ensures immutability, and dispatches the billing asset securely.
Strategic Next Steps
Connecting an AI agent to a rigid, compliance-heavy accounting platform like Pennylane is not about executing blind write operations. It requires a resilient integration layer that understands the underlying API's pagination limits, synchronous constraints, and authentication requirements.
By leveraging Truto's dynamically generated MCP servers, you eliminate the need to write and maintain JSON schemas, handle OAuth refreshes, or build routing middleware. You simply configure your security perimeters, pass the tokenized URL to your agent framework, and focus on designing the prompts that drive complex financial orchestration.
FAQ
- Does Truto automatically retry Pennylane API rate limits?
- No. Truto does not retry, throttle, or apply backoff on rate limit errors. When Pennylane returns an HTTP 429, Truto passes that error directly to the caller. Truto normalizes upstream rate limit info into standardized headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset) per the IETF spec, leaving the caller responsible for retry logic.
- How are Pennylane MCP tools generated?
- Truto dynamically derives MCP tools from the integration's resource definitions and documentation records. A tool only appears if it has a corresponding description and JSON schema. Tools are never cached; they are generated on every tools/list request.
- Can I limit the MCP server to only read operations?
- Yes. When creating the MCP server via the Truto API or UI, you can apply method filters such as config: { methods: ["read"] }. This restricts the server to only get and list operations, protecting sensitive write actions like finalizing invoices or lettering ledger lines.
- How do I secure the Pennylane MCP server?
- Each server URL contains a cryptographically hashed token. You can enforce a second layer of security by setting require_api_token_auth to true, which requires the MCP client to also pass a valid Truto API token. You can also define an expires_at timestamp to create temporary, auto-expiring access.