Installation
Requirements
- Craft CMS 5.3 or later
- Craft Commerce 5.0 or later
- PHP 8.2 or later
- A Holded account on a paid plan — the API is not available on Holded's free tier
Install
composer require justinholtweb/craft-holding
php craft plugin/install holding
Or find Holding in the Craft Plugin Store and install it from there.
Nothing is sent until you say so
Holding installs with Send orders automatically switched off. A plugin that starts filing documents in somebody's live books the moment it is installed is a plugin that gets uninstalled, so until you turn that on, the only orders that reach Holded are the ones you send by hand.
Get an API token
In Holded: Settings → Developers → Credentials → Add API Token. Give it invoicing permissions.
Put it in your .env rather than in the settings field directly:
HOLDED_API_KEY="your-token"
and reference it in Settings → Plugins → Holding as $HOLDED_API_KEY. Craft resolves the
variable at runtime, so the token never enters project config and never reaches your repository.
Press Test the connection. Holding tries the key header first and falls back to
Authorization: Bearer if that is refused, then tells you which one your token wanted — tokens
minted in Holded's newer Developers screen behave differently from older ones, and it is not worth
your time to work out which kind you were given.
Read Holded's own lists
Press Re-read Holded's lists. That fetches your numbering series, bank accounts, payment methods, sales channels, warehouses and tax definitions, and fills in the dropdowns below with your account's real values instead of asking you to paste ids.
The lists are cached for an hour. Press it again after you add a numbering series in Holded.
Map your VAT rates
Under VAT, map each rate you charge to the Holded tax key that means it:
| Rate | Holded tax key |
|---|---|
| 21 | s_iva_21 |
| 10 | s_iva_10 |
| 4 | s_iva_4 |
| 0 | s_iva_0 |
Your own keys are listed in Holded under Settings → Billing → Taxes. If Holding has read them, the right-hand column is a dropdown of your real taxes rather than a text field.
A rate with no key mapped is sent as a plain percentage instead, and the document is flagged so you know it happened.
Send one order, by hand
Open a completed order in the control panel. Holding's panel sits under the order details and shows you the VAT treatment it would use and the reasoning behind it, before anything is sent.
Press Send to Holded. Then open the document in Holded and check it against the order: the total, the tax, the contact, the numbering series.
When that one looks right, turn on Send orders automatically and pick a trigger.
Backfill the rest
php craft holding/sync/preview <order> # what would be sent, without sending it
php craft holding/sync/all --since=2026-01-01 --limit=500
preview is worth running on a handful of your more awkward orders first — an export, an EU
business, a refunded one — before you send several hundred at once.