Sync Zoho Books Invoices into Zoho Creator Using a Webhook

Push Zoho Books Records into Zoho Creator with a Webhook

Some data only exists on the Creator side — a tax identifier captured at signup, a project code, an internal reference. This pattern lets Zoho Books push an invoice into Creator by webhook, then writes the missing value straight back onto the Books invoice as a custom field. Two systems, one round trip.

Rewritten for current auth and against a duplicate bug. Zoho retired legacy authtokens entirely — deprecation was announced in December 2020 and every existing token stopped working on 1 April 2021. Any script still passing ?authtoken= has been failing for years. Everything now goes through OAuth 2.0 with a named Zoho connection. The legacy getUrl() and postUrl() tasks go with it — use invokeurl or the native integration tasks. In this script the specific break is zoho.books.updateRecord being called without a connection argument, which is now mandatory for the Books tasks. The original also built its payload by concatenating a JSON string and calling .toMap() on it — any quote or accent in a tax field would have broken the parse. And it had three near-identical if/else branches that all resolved to the same rule: use the DNI/NIE when present, otherwise the CIF. That is now two branches.

Before you start

  • A webhook in Zoho Books (Settings → Automation → Webhooks) pointing at your Creator form’s API endpoint.
  • A Zoho OAuth connection named zoho_books_connection with ZohoBooks.invoices.UPDATE.
  • An Invoices form with Invoice_ID, DNI_NIE and CIF.
  • The custom field already created in Books, and its index.

Deluge script

// ── On Add > On Validate ─────────────────────
// Zoho Books webhook posts the invoice into Creator.

// 1. Reject duplicates from webhook retries
if(Invoices[Invoice_ID == input.Invoice_ID].count() > 0)
{
    cancel submit;
}

// 2. Require at least one tax identifier
taxId = "";
if(input.DNI_NIE != null && input.DNI_NIE != "")
{
    taxId = input.DNI_NIE;
}
else if(input.CIF != null && input.CIF != "")
{
    taxId = input.CIF;
}

if(taxId == "")
{
    cancel submit;
}

// 3. Push the tax id back onto the Books invoice as a custom field
customField = Map();
customField.put("index",2);
customField.put("label","NIF / CIF / NIE");
customField.put("value",taxId);

customFields = List();
customFields.add(customField);

updateMap = Map();
updateMap.put("custom_fields",customFields);

resp = zoho.books.updateRecord("invoices","YOUR_BOOKS_ORG_ID",input.Invoice_ID,updateMap,"zoho_books_connection");

if(resp.get("invoice") == null)
{
    info "Books update failed: " + resp.toString();
}

Notes

  • The duplicate check is not optional. Webhooks retry. If the first delivery succeeded but the acknowledgement was lost, Books sends it again — and without the count check you get two records for one invoice. This is the single most common webhook bug.
  • On Validate, so you can still refuse. cancel submit only works before the record is written. On Success is too late.
  • Custom fields are matched by index. The index is the field’s position in the Books custom-field list, and it is easy to break by reordering fields later. Some endpoints accept customfield_id instead, which is stable — prefer it if your call supports it.
  • Module names are lowercase. "invoices", not "Invoices". The error you get for the wrong case is not helpful.

This script is part of the free Creator Scripts Deluge Library.

All 39 Deluge scripts, the full Zoho Creator course, and every downloadable asset are now free. Get free access →