Holding for Craft CMS

Configuration

Connection

SettingWhat it does
API keyYour Holded token. Use an env variable.
How the key is sentkey header or Authorization: Bearer. Holding tries the other one automatically if the first is refused, so this only decides which it tries first.
API base URLOnly change this if Holded moves.
TimeoutSeconds before a call is abandoned.
Calls per minuteA ceiling on outbound calls. Holded meters API use by plan, over a month, so this is not about avoiding a 429 — it is about a backfill of six months of orders not spending the month's allowance in an afternoon.

Documents

What to create — an invoice (factura) is the usual answer for a shop. A sales receipt (ticket) suits a till; a sales order (pedido) suits a business that invoices separately later.

When — on order completion, on full payment, on reaching a chosen order status, or never (by hand only). Whichever you pick, the work happens in the queue: a checkout must not wait on Holded, and must not fail because Holded is down.

Approve documents on creation — leave this on. Holded files an unapproved document as a draft, and a draft is invisible in Sales → Invoices, on the contact, and in search. It exists, but nobody can find it.

The trade is that Holded locks a document permanently once it is approved. That is why Holding refuses to re-send an approved document rather than trying and half-succeeding: to change one, you issue a credit note, or delete it in Holded first.

Numbering series — which of your Holded series numbers the document. Blank uses whatever Holded considers the default.

Document date — the date the document carries. For a Spanish VAT return the date the sale happened is normally what matters, not the date you got round to syncing it.

Line subtotal — whether a line's subtotal is the price per unit or the line total. Holded's own reading is the unit price, which is the default. Either way Holding reads the created document back and compares its total with the order's, so a wrong choice here shows up as a flagged document rather than a wrong VAT return.

Check the total afterwards — leave this on. It costs one extra API call per document and it is the single most useful thing this plugin does.

Customers

Where the NIF/CIF comes from — Craft's address element carries organizationTaxId, which is where Commerce's own address form puts a tax id and where Holding looks by default. If you keep it in a custom field instead, name the handle; Holding checks the address first, then the order.

Update contacts that already exist — off by default, and think before turning it on. Holded is often where the accountant maintains customer details, and this lets a checkout overwrite them.

Refuse to sync an invalid Spanish tax id — off by default. A NIF whose check letter does not compute is a warning on the document; turn this on if you would rather the order stopped and waited for a human.

VAT

This is the part worth reading twice. See VAT and the rules for what each treatment means; the settings themselves are:

SettingNotes
Home countryThe country you invoice from. Everything else is judged relative to it.
Send Holded tax keysOn, each line carries a key from your Holded tax list. Off, it carries a plain percentage and Holded decides what that means.
Exempt tax keyThe key for a line carrying no VAT because the sale is exempt.
Reverse charge / Export / Excluded territoriesEach can be switched off, and each has its own legal wording.
One Stop ShopCharge EU consumers their own country's VAT.
VIESCheck VAT numbers against the European Commission's register.
Recargo de equivalenciaThe surcharge Spanish retailers under the regime pay.
IRPF retentionA percentage withheld on the whole document. 0 switches it off.

The legal wording fields are editable because the correct phrasing is a matter for your accountant, not for a plugin. The defaults cite the articles usually cited.

One Stop Shop rates

Holding ships the EU standard rates and the settings screen tells you the date they were last checked. Rates move. Anything you put in the override table wins.

Turning OSS on does not change what Commerce charges. Set your Commerce tax rates to match, and Holding will tell you when they have drifted apart.

VIES

VIES is the only thing that can tell you a VAT number is really registered, and it is regularly unavailable — individual member states' registers go down for hours. So an answer is one of three things and never two: valid, invalid, or unknown.

What happens on unknown is your decision:

  • Charge VAT — the safe-for-you option. You may charge VAT a business did not owe.
  • Accept a well-formed number — the safe-for-them option. You may fail to charge VAT that was owed.

Both are expensive, in different directions, which is why Holding will not pick for you.

Payments

Register payments against the document — leave this on, or every invoice sits in Holded as unpaid and somebody starts chasing customers who have already paid.

Holded treats how the customer paid and where the money landed as two different things. The payment method goes on the payment; the bank account is attached in a follow-up call, because Holded's /pay endpoint will not take one. Map both.

Per-gateway accounts — gateway handle → bank account and payment method. Anything not listed uses the defaults.

Refunds

A refund in Spain is its own document — a factura rectificativa — not an edit to an invoice that is already locked. Each successful Commerce refund transaction becomes one credit note, keyed to that transaction, so two partial refunds are two credit notes rather than one overwriting the other.

Products and stock

Matched on SKU and nothing else. Renaming a product in either system is fine. Changing a SKU deliberately creates a new Holded product rather than re-pointing an existing one, because in an accounting system a SKU is an identity and silently moving one corrupts a stock history.

What a stock read does defaults to report, which records the difference for a human to look at. Apply makes Holded the authority and writes its numbers into Commerce — right for a warehouse-run business, wrong for everybody else. Applying stock needs Commerce 5.3 or later, where stock moved into the inventory system.

Customer invoice download

Off by default. On, a customer can fetch the real PDF from Holded through your own site. The link requires the order's full number and, unless you turn that off, the email address on the order; failures are throttled per IP and every failure answers identically.

An invoice carries a name, a postal address and a tax id. Turning the email requirement off makes the order number the only thing standing between a stranger and all three.

When things go wrong

Attempts and retry delay control how many times a failed document is retried before it waits for a human. Put holding/sync/retry on cron and it will pick those up.

Keep request and response bodies is invaluable when Holded rejects something, and does mean the log holds customer names, addresses and tax ids. Set a retention that suits you and prune.