Accounting
Ledger entries, expense and accounting accounts, tax rates and numbering series.
One consolidated base URL, API-key authentication, cursor pagination and structured errors. Here is what v2 covers, how to authenticate, working code examples, and the real pitfalls when you build or migrate a production integration.
Holded consolidated what used to be several product APIs into a single v2 surface with clear sections. The legacy v1 remains accessible as an archived reference and existing integrations keep working, but all new development targets v2 — and, per Holded's own docs, new integrations should use it directly.
v2 uses a per-account API key rather than an OAuth flow for server-to-server integrations. Generate the key in Holded (Settings → Developers), then send it on every request in the key header. The key inherits the permissions of its account, so scope it deliberately.
The API is organised into sections that map to the ERP. Each exposes list, read, create, update and delete operations, plus bulk actions and native attachments where they apply.
Ledger entries, expense and accounting accounts, tax rates and numbering series.
Banking accounts, cash and bank movements, payments, payment methods and remittances.
Invoices, estimates, proformas, credit notes, sales orders, receipts and waybills — with bulk operations.
Products, services, price lists, warehouses, stock levels and in-transit stock, multi-warehouse CRUD.
Contacts and groups, leads, funnels and configurable pipelines shared with your documents.
Projects and time tracking, employees, contracts, events, tasks and the shared inbox.
A minimal read against v2: authenticate with the key header, page with a cursor, and back off on rate limits. The pattern is identical for products, contacts and every other list endpoint.
# List invoices — cursor pagination
curl -s "https://api.holded.com/api/invoicing/v2/documents/invoice?limit=100" \
-H "accept: application/json" \
-H "key: <YOUR_API_KEY>"
# Follow the cursor returned in the response
curl -s "https://api.holded.com/api/invoicing/v2/documents/invoice?limit=100&cursor=<NEXT_CURSOR>" \
-H "accept: application/json" \
-H "key: <YOUR_API_KEY>"// Cursor loop — pseudo-JS, works the same in any language
let cursor = undefined;
const invoices = [];
do {
const res = await fetch(
`https://api.holded.com/api/invoicing/v2/documents/invoice?limit=100` +
(cursor ? `&cursor=${cursor}` : ""),
{ headers: { accept: "application/json", key: process.env.HOLDED_API_KEY } }
);
if (res.status === 429) { await backoff(); continue; } // respect rate limits
const page = await res.json();
invoices.push(...page.data);
cursor = page.nextCursor; // undefined when there are no more pages
} while (cursor);It is not just the URL. These structural changes break code silently if you do not review each one.
Several product surfaces (Invoice, CRM, Projects, Team, Accounting APIs)
One consolidated base URL with sections (Accounting, Treasury, Sales, Inventory, Contacts, CRM, Projects, Team & HR, Calendar, Inbox)
Change your base URL and route prefixes. Mostly find-and-replace, but review every call.
Offset pagination (page=1, page=2, …)
Cursor pagination (opaque next cursor)
Efficient on large lists, but breaks any code that jumped to page N or computed a total page count.
Ad hoc error messages per endpoint
Structured errors with consistent codes
Your old error handling may not recognise the new shapes. Mandatory review.
Mostly individual operations
Bulk operations for invoices (approve, cancel, delete in batches)
Clear win for large loads — but watch idempotency on partial retries.
Basic inventory
Multi-warehouse with stock levels, in-transit stock and full warehouse CRUD
Cases that needed external logic in v1 are now a single native endpoint.
No standard protocol for AI agents
MCP (Model Context Protocol) server announced as coming soon
Your assistant can read and act on Holded without bespoke REST wrappers.
We maintain an n8n community node for Holded with 100% coverage of API v2 — 42 resources · 323 operations. It is published on npm with signed provenance attestation from GitHub Actions, no manual tokens. If you want to automate against Holded without writing a full integration, it is free to start and needs no consulting.
When your case is more demanding than an n8n workflow — high volume, idempotency under concurrency, tax logic, SLAs — we build custom integrations directly against the v2 API. The node is a strong starting point and, sometimes, the whole solution.
An API integration that writes data cannot be flipped over in one shot. What we do on every project:
Holded uses a per-account API key sent as a request header on every call. You generate the key from Holded (Settings → Developers → API key) and pass it in the key header. There is no OAuth handshake for server-to-server integrations, so treat the key like a password: store it in a secret manager, never ship it to the browser, and rotate it if it leaks. Each key inherits the permissions of the account it belongs to.
Three structural changes affect existing code. First, a single consolidated base URL with sections instead of several product-specific APIs. Second, cursor pagination (an opaque next cursor) instead of numbered offset pages, which is far more efficient on large lists but breaks any loop that assumed page numbers. Third, structured error responses with consistent codes instead of ad hoc per-endpoint messages. v2 also adds bulk operations, multi-warehouse inventory with in-transit stock, recurring invoices with a queryable calendar and native attachments.
List endpoints return an opaque next cursor rather than a page number. You request the first page, read the cursor from the response, and pass it back on the next call until no cursor is returned. This is stable under concurrent writes and cheap on large datasets, but it means you cannot jump to page N or compute a total page count up front. If you are porting from v1, rewrite offset loops as cursor loops and add a test that walks a full multi-page result.
Holded documents webhooks as coming soon. Until they ship, robust integrations rely on cursor-based polling (much cheaper than v1 offset polling) or on triggers from the source system (Shopify, Stripe, and so on) that feed a persisted queue. When native webhooks arrive, you migrate one component at a time from polling to webhook without touching the rest of the flow.
MCP is the Model Context Protocol, an open standard from Anthropic that lets AI agents access external systems through typed tools with controlled permissions. A native Holded MCP server means any connected assistant (Claude, or ChatGPT via adapters, or a custom agent) can read invoices, products or ledger entries without you writing a REST wrapper per use case. If you are planning an AI assistant on top of Holded, it is usually worth waiting for the native MCP instead of building plumbing that will be obsolete.
Holded throttles per API key, so design for retries from day one. Respect 429 responses with exponential backoff, batch reads with the largest page size the endpoint allows, and cache reference data (products, contacts, tax rates) that does not change every minute. Because v2 returns structured errors with consistent codes, you can branch on the code rather than parsing free-text messages, which makes retry and idempotency logic much cleaner.
Yes. We build new integrations directly on v2 and migrate existing v1 integrations with a parallel period: your v1 flow keeps producing while the v2 flow writes to staging until both reconcile. Scope and a fixed price come after a free 30-minute diagnostic where we map exactly which endpoints you touch, whether your integration writes (higher risk) or only reads, and where idempotency needs attention.
In 30 minutes we map your endpoints, flag the real risks — pagination, idempotency, rate limits — and tell you whether to build new or migrate.