FrancodesystemsFrancodesystems
Developer guide · API v2 (current)

Integrating with the Holded API v2.

By Juan Cantón Rodríguez · Updated

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.

What v2 is

One API for the whole ERP

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.

What v2 brings
  • → Single consolidated base URL
  • → API-key header authentication
  • → Cursor pagination (not offset)
  • → Consistent structured errors
  • → Bulk operations (approve / cancel / delete)
  • → Multi-warehouse with in-transit stock
  • → Native attachments on every document
  • → Signed webhooks
  • → Official MCP connector
Authentication

A single API key, sent as a header

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.

  • 1Keep the key server-side. Never expose it in a browser or mobile client.
  • 2Store it in a secret manager and rotate it if it is ever exposed.
  • 3Respect rate limits with exponential backoff on 429 responses.
Resources & endpoints

What the v2 surface covers

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.

Accounting

Ledger entries, expense and accounting accounts, tax rates and numbering series.

Treasury

Banking accounts, cash and bank movements, payments, payment methods and remittances.

Sales

Invoices, estimates, proformas, credit notes, sales orders, receipts and waybills, with bulk operations.

Inventory

Products, services, price lists, warehouses, stock levels and in-transit stock, multi-warehouse CRUD.

Contacts & CRM

Contacts and groups, leads, funnels and configurable pipelines shared with your documents.

Projects, Team & Calendar

Projects and time tracking, employees, contracts, events, tasks and the shared inbox.

Code examples

List invoices and follow the cursor

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.

curl
# 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>"
javascript
// 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);
v1 vs v2

What actually breaks when you migrate

It is not just the URL. These structural changes break code silently if you do not review each one.

v1

Several product surfaces (Invoice, CRM, Projects, Team, Accounting APIs)

v2

One consolidated base URL with sections (Accounting, Treasury, Sales, Inventory, Contacts, CRM, Projects, Team & HR, Calendar, Inbox)

Impact

Change your base URL and route prefixes. Mostly find-and-replace, but review every call.

v1

Offset pagination (page=1, page=2, …)

v2

Cursor pagination (opaque next cursor)

Impact

Efficient on large lists, but breaks any code that jumped to page N or computed a total page count.

v1

Ad hoc error messages per endpoint

v2

Structured errors with consistent codes

Impact

Your old error handling may not recognise the new shapes. Mandatory review.

v1

Mostly individual operations

v2

Bulk operations for invoices (approve, cancel, delete in batches)

Impact

Clear win for large loads, but watch idempotency on partial retries.

v1

Basic inventory

v2

Multi-warehouse with stock levels, in-transit stock and full warehouse CRUD

Impact

Cases that needed external logic in v1 are now a single native endpoint.

v1

No standard protocol for AI agents

v2

Official MCP connector (August 2026), with a read-only and an operational profile

Impact

Claude or ChatGPT can query Holded without bespoke REST wrappers; invoices and accounting stay read-only.

Public proof of coverage

We covered the entire v2 API and open-sourced it

We maintain an n8n community node for Holded with 100% coverage of API v2: 43 resources · 327 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.

How we build it

Production integrations, with a parallel period

An API integration that writes data cannot be flipped over in one shot. What we do on every project:

  • Full inventory of the v1 endpoints your current integration touches
  • Endpoint-by-endpoint mapping to the v2 equivalent, with change notes
  • Pagination rewrite: offset loops → cursor loops, with tests
  • Error handling rewrite for the new structured codes
  • Staging that receives the same traffic as production for 1-2 weeks
  • Output-equality validation, v1 vs v2, before cut-over
  • A documented rollback plan in case anything looks wrong after cut-over
FAQ

Holded API v2: developer FAQ

How do I authenticate against the Holded API v2?

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.

What changed between API v1 and v2?

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.

How does pagination work in v2?

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.

Are there webhooks in v2?

Yes. Holded now sends a webhook when a contact, invoice or payment is created, updated or deleted, signed with HMAC so you can check it really comes from Holded. Not everything has an event (there is none for an invoice falling due), so for that we still run a periodic cursor-based query. One thing to get right: deduplicate every delivery, because a webhook can arrive more than once.

What is the Holded MCP server?

MCP is the Model Context Protocol, an open standard from Anthropic that lets AI agents access external systems through typed tools with controlled permissions. Holded released its official MCP connector in August 2026 (mcp.holded.com) with two profiles: a read-only analytics profile and an operational one that can create contacts, quotes or projects, while accounting, invoices and taxes always stay read-only. For querying your data from Claude or ChatGPT it is often enough; custom work makes sense when you need unattended processes, validation against your own rules or controlled writes.

What are the rate limits and how should I handle errors?

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.

Can you build or migrate our Holded v2 integration?

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.

Building on the Holded API v2?

In 30 minutes we map your endpoints, flag the real risks (pagination, idempotency, rate limits) and tell you whether to build new or migrate.