Create a Zoho Books Invoice from a Zoho Creator Record with Deluge

Sync Zoho Creator to Zoho Books and Zoho Invoice

When the quote is approved in Creator, the invoice should already exist in Books. This function finds the customer (creating them if this is their first order), turns a Creator subform into invoice line items, and raises the invoice — writing the invoice number back so the two records stay linked.

The original cannot authenticate any more. 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. The original also posted to books.zoho.com/api/v3, which is superseded by www.zohoapis.com/books/v3, and wrapped its payload in the old JSONString= form-encoding. All three are replaced below by the native zoho.books.* tasks, which handle the host, the auth and the encoding for you.

Before you start

  • A Zoho OAuth connection named zoho_books_connection with ZohoBooks.contacts.CREATE, ZohoBooks.contacts.READ and ZohoBooks.invoices.CREATE.
  • Your Books organization ID — it is required on every single call.
  • Creator forms: Invoice (with an Item_Detail subform), Contact with a Zoho_Books_Contact_ID field, and Item with Zoho_Books_Item_ID.

Deluge function

// ============================================
// CREATOR -> ZOHO BOOKS / INVOICE
// Find or create the contact, then raise the invoice.
// ============================================

void ZohoBooks.createInvoice(int recordId)
{
    ORG_ID = "YOUR_BOOKS_ORG_ID";
    CONN   = "zoho_books_connection";

    rec      = Invoice[ID == recordId];
    customer = Contact[ID == rec.Contact];

    // --- 1. Reuse the stored contact id, or create the contact ---
    contactId = customer.Zoho_Books_Contact_ID;
    if(contactId == null || contactId == "")
    {
        newContact = Map();
        newContact.put("contact_name",customer.Company_Name);
        newContact.put("contact_type","customer");
        newContact.put("email",customer.Email);

        createResp = zoho.books.createRecord("contacts",ORG_ID,newContact,CONN);
        contactBlock = createResp.get("contact");
        if(contactBlock == null)
        {
            info "Books contact creation failed: " + createResp.toString();
            return;
        }
        contactId = contactBlock.get("contact_id");
        customer.Zoho_Books_Contact_ID = contactId;
    }

    // --- 2. Build the line items from the Creator subform ---
    lineItems = List();
    for each row in rec.Item_Detail
    {
        itemRec = Item[ID == row.Item_Name];

        lineItem = Map();
        lineItem.put("item_id",itemRec.Zoho_Books_Item_ID);
        lineItem.put("name",itemRec.Item_Name);
        lineItem.put("rate",row.Rate);
        lineItem.put("quantity",row.Quantity);
        lineItems.add(lineItem);
    }

    if(lineItems.size() == 0)
    {
        info "No line items on record " + recordId + " - nothing to invoice.";
        return;
    }

    // --- 3. Raise the invoice ---
    invoiceMap = Map();
    invoiceMap.put("customer_id",contactId);
    invoiceMap.put("line_items",lineItems);
    invoiceMap.put("date",zoho.currentdate.toString("yyyy-MM-dd"));
    invoiceMap.put("due_date",zoho.currentdate.addDay(30).toString("yyyy-MM-dd"));
    invoiceMap.put("reference_number","CR-" + recordId);

    invoiceResp = zoho.books.createRecord("invoices",ORG_ID,invoiceMap,CONN);
    invoiceBlock = invoiceResp.get("invoice");

    if(invoiceBlock != null)
    {
        rec.Books_Invoice_ID     = invoiceBlock.get("invoice_id");
        rec.Books_Invoice_Number = invoiceBlock.get("invoice_number");
        info "Invoice created: " + rec.Books_Invoice_Number;
    }
    else
    {
        info "Books invoice creation failed: " + invoiceResp.toString();
    }
}

Notes

  • Store the contact id on your side. Searching Books for a customer on every invoice is a wasted call and a race condition waiting to happen — two quick submissions create two duplicate contacts. Save contact_id to the Creator record the first time and reuse it.
  • Zoho Invoice is the same code, one word different. Swap zoho.books.createRecord for zoho.invoice.create and the scopes for ZohoInvoice.*. Note the Invoice task is create, not createRecord, and its connection argument is not supported from Creator — Creator uses the app’s built-in Zoho Invoice integration instead.
  • Check the response block, not the string. Reading .get("invoice") and testing for null is more reliable than searching the raw response for a substring.
  • Guard the empty subform. Books rejects an invoice with no line items with an error that does not obviously say so. Catching it in Creator saves the confusion.

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 →