npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@getpopapi/n8n-nodes-pop-zoho

v0.1.0

Published

n8n custom node — maps POP API payload to Zoho Invoice / Zoho Books

Readme

n8n-nodes-pop-zoho

POP Cloud API License n8n community

An n8n community node that receives a POP API payload and automatically creates the corresponding document in Zoho Invoice or Zoho Books.

Supported document types:

  • TD01 → Invoice (POST /invoices)
  • TD04 → Credit Note (POST /creditnotes)


Table of Contents


How It Works

The node sits inside a standard n8n webhook workflow. POP API calls the webhook with a signed payload; the node verifies the request, maps the POP fields to Zoho's data model, resolves or creates the customer contact, and creates the invoice or credit note via Zoho's REST API.

POP API ──► Webhook node ──► POP → Zoho ──► Respond to Webhook node

POP API handles the entry point (license validation, sandbox/live mode, quota billing). This node owns: signature verification, payload mapping, contact resolution, tax lookup, and Zoho API calls.

The connector can be triggered in two ways (see Trigger Paths):

  • Via webhook panel — used by WooCommerce, Shopify, and any platform that calls a document-generation route
  • Via direct /connector/zoho route — used for Postman, direct API calls, and LLM/MCP agents

Prerequisites

POP API

  • An active POP API account with a valid license key
  • The n8n Connector enabled in your POP API account settings
  • The n8n webhook URL registered in your POP API webhook panel

Zoho

  • A Zoho Invoice or Zoho Books account
  • An OAuth2 application created in the Zoho API Console (or the console for your region)
  • Organization ID — found in Zoho → Settings → Organization Profile
  • Tax rates configured in Zoho → Settings → Taxes, one entry per percentage used in your POP payloads (e.g. 22%, 10%, 4%, 0%)

n8n

  • n8n self-hosted (v2.x or later) or n8n Cloud Enterprise (custom nodes required)
  • Node.js v18+

Installation

From npm (recommended)

In your n8n instance go to Settings → Community Nodes → Install and enter:

n8n-nodes-pop-zoho

Manual / local development

# Clone the repository
git clone https://github.com/getpopapi/n8n-nodes-pop-zoho.git
cd n8n-nodes-pop-zoho

# Install dependencies and build
npm install
npm run build

# Symlink into n8n custom nodes directory
ln -s $(pwd)/dist/nodes/PopZohoInvoice/PopZohoInvoice.node.js ~/.n8n/custom/PopZohoInvoice.node.js
ln -s $(pwd)/dist/credentials/ZohoInvoiceOAuth2Api.credentials.js ~/.n8n/custom/ZohoInvoiceOAuth2Api.credentials.js
ln -s $(pwd)/dist/nodes/PopZohoInvoice/pop-zoho-invoice.svg ~/.n8n/custom/pop-zoho-invoice.svg

# Restart n8n
n8n start

Credentials Setup

1. Create the Zoho OAuth2 Application

  1. Go to the Zoho API Console for your region
  2. Create a new Server-based Application
  3. Set the redirect URI to your n8n OAuth callback URL: https://<your-n8n-instance>/rest/oauth2-credential/callback
  4. Note the Client ID and Client Secret

2. Configure the Credential in n8n

In n8n go to Credentials → Add Credential → Zoho OAuth2 (Invoice / Books) and fill in:

| Field | Description | |-------|-------------| | Product | Zoho Invoice or Zoho Books — use Books for UAE e-invoicing | | Region | Your Zoho data center (EU, US, IN, AU, JP, CA) | | Organization ID | From Zoho → Settings → Organization Profile | | Authorization URL | Auto-filled based on region — e.g. https://accounts.zoho.eu/oauth/v2/auth | | Access Token URL | Same host as Authorization URL, path /oauth/v2/token | | Client ID | From your Zoho API Console application | | Client Secret | From your Zoho API Console application | | Scope | Pre-filled with all required scopes — do not change unless you know what you are doing |

Click Connect to complete the OAuth2 authorization flow. Zoho will ask you to confirm the requested permissions.

Note on scopes: The pre-filled scope string includes all permissions required for full connector operation: invoices, credit notes, contacts, and settings. If you previously connected with a narrower scope, revoke the authorization from your Zoho account → Connected Apps, then reconnect to grant the updated permissions.


Workflow Setup

Recommended Workflow

[Webhook node]
    ↓
[POP → Zoho node]
    ↓
[Respond to Webhook node]
  1. Webhook node — set Authentication to None (security is handled by the POP → Zoho node via HMAC + RSA JWT). Set Respond to Using 'Respond to Webhook' Node. Set the path to webhook/zoho.
  2. POP → Zoho node — configure as described below.
  3. Respond to Webhook node — leave default settings; it will forward the node output as the HTTP response.

Register the Webhook URL in POP API

Copy the Production URL from the Webhook node (e.g. https://your-n8n.example.com/webhook/zoho) and paste it into your POP API account → Webhooks panel. Note the webhook ID (e.g. popWh_xxxxxxxx) — you will need it in every API call.

Webhook path naming: Use webhook/zoho as the path in the n8n Webhook node. The node supports both Zoho Invoice and Zoho Books, so a product-neutral path avoids confusion and matches the connector slug used by POP API.


Node Parameters

Security

| Parameter | Description | |-----------|-------------| | POP API URL | Base URL of your POP API instance (default: https://popapi.io). Used to auto-fetch the RSA public key. | | POP API License Key | Your default POP API license key. Used to verify the HMAC signature on every incoming request. | | POP API RSA Public Key | Click the Refresh button to auto-fetch the public key from your POP API instance. Required for RSA JWT verification. |

Which license key to use: Always use the default API key — found in your POP API account at popapi.io → Account → API → API Key (default). Do not use platform-specific keys (e.g. the key generated for a Shopify or WooCommerce integration). POP API always signs connector requests with the default key regardless of which platform triggered the call, so the node must verify against that same key.

How to get the RSA Public Key: Set the POP API URL field, then click the refresh icon (🔄) next to POP API RSA Public Key. The key is fetched automatically from your POP API instance. You only need to do this once per credential setup (or after a key rotation on the POP API server).

Zoho Behavior

| Parameter | Default | Description | |-----------|---------|-------------| | Contact Match Strategy | VAT → Email → Name | How to find an existing Zoho contact for the invoice customer. | | Create Contact If Missing | true | Automatically create the contact in Zoho when no match is found. | | Invoice Status | Draft | Status to set on the created invoice in Zoho (Draft or Sent). | | Send Email to Customer | false | Trigger Zoho's built-in invoice email to the customer after creation. | | Place of Supply | Not specified | Required for UAE e-invoicing — select the emirate. Leave empty for standard invoices. | | Deferred Payment Terms (days) | 30 | Days granted for payment when the payload uses deferred payment terms (TP02). |

Other

| Parameter | Default | Description | |-----------|---------|-------------| | Dry Run | false | Validate the payload and return the Zoho body that would be sent, without making any Zoho API call. Useful for testing field mapping. |


Trigger Paths

The connector can be triggered in two distinct ways depending on the client.

Path A — Via Webhook Panel (WooCommerce, Shopify, any platform using document-generation routes)

The client calls one of POP API's document-generation routes (create-xml, create-ubl, create-ksef-xml) with a webhook integration. POP API looks up the webhook entry, sees that connector_type: zoho is set, and automatically routes the call through the connector.

Setup steps:

  1. Register the n8n webhook URL in the POP API Webhooks panel
  2. Set the Connector field on that webhook entry to n8n Connector — Zoho
  3. In your API call, pass integration.use: "webhook" and integration.id: "<webhook-id>"

Request example:

{
  "integration": {
    "use": "webhook",
    "id": "<webhook-id>"
  },
  "environment": "sandbox",
  "data": { ... }
}

This path is used automatically by the WooCommerce and Shopify integrations — no extra configuration is needed on the client side once the webhook panel is set up.


Path B — Direct /connector/zoho Route (Postman, API, LLM/MCP)

The client calls POP API's dedicated connector endpoint directly. The webhook entry still needs to exist (to store the n8n URL), but the routing is explicit.

Request:

POST https://popapi.io/wp-json/api/v2/connector/zoho

Headers:
  Content-Type: application/json
  X-Api-Key: <your-default-license-key>

Body:

{
  "integration": {
    "use": "n8n-zoho",
    "id": "<webhook-id>"
  },
  "environment": "sandbox",
  "data": {
    "invoice_body": {
      "general_data": {
        "doc_type": "TD01",
        "date": "2026-03-04",
        "invoice_number": "2026/0001",
        "currency": "EUR"
      }
    },
    "transferee_client": {
      "personal_data": {
        "company_name": "Cliente Test SRL",
        "email": "[email protected]",
        "tax_id_vat": {
          "id_code": "12345678901",
          "country_id": "IT"
        },
        "tax_id_code": "RSSMRA80A01H501U"
      },
      "place": {
        "address": "Via Milano 10",
        "city": "Milano",
        "zip_code": "20100",
        "province_id": "MI",
        "country_id": "IT"
      }
    },
    "order_items": [
      {
        "description": "Servizio consulenza",
        "quantity": "1.00",
        "unit_price": "100.00",
        "rate": "22.00",
        "unit": "N.",
        "item_code": { "type": "SKU", "value": "PROD-001" },
        "discount_percent": "",
        "discount_amount": ""
      }
    ],
    "payment_data": {
      "terms_payment": "TP02",
      "payment_details": "MP05",
      "payment_amount": "122.00"
    },
    "purchase_order_data": { "id": "#1001", "date": "2026-03-04" },
    "connected_invoice_data": { "id": "", "date": "" }
  }
}

| Field | Values | Description | |-------|--------|-------------| | integration.id | your webhook ID | The webhook ID registered in your POP API account | | environment | sandbox / live | sandbox runs a dry-run — no Zoho calls, no quota deducted | | data.invoice_body.general_data.doc_type | TD01 / TD04 | Document type: TD01 = invoice, TD04 = credit note |

For TD04 credit notes, include connected_invoice_data.id with the Zoho invoice ID of the original document being reversed (returned as zoho_invoice_id in the invoice creation response).

Successful Response

{
  "success": true,
  "environment": "live",
  "connector": "zoho",
  "message": "Invoice created successfully.",
  "status_code": 200,
  "data": {
    "success": true,
    "zoho_product": "books",
    "zoho_document_type": "invoice",
    "zoho_invoice_id": "994269000000070002",
    "zoho_invoice_number": "INV-000001",
    "zoho_status": "draft",
    "zoho_total": 1220.00,
    "contact_id": "994269000000062001",
    "contact_created": false
  }
}

Consumer Guides

WooCommerce

  1. In your POP API account → Webhooks panel, add the n8n webhook URL and set the Connector field to n8n Connector — Zoho.
  2. In your WooCommerce numeration settings, select the webhook entry you just created.
  3. When an order is processed, POP API automatically calls the connector — no changes needed in WooCommerce.
  4. The connector is triggered both during AJAX (immediate) and scheduled cron flows.

The license key configured in the WooCommerce plugin is the standard POP API key. POP API always signs with the default key, so the n8n credential must use that same key.


Shopify

  1. Register the n8n webhook URL in the POP API webhook panel and set Connector to n8n Connector — Zoho.
  2. In the Shopify app, select that webhook entry in the numeration settings for the relevant document type.
  3. When the Shopify app generates a document, POP API routes the call through the connector automatically.

The Shopify integration uses a platform-specific license key internally, but POP API always signs connector requests with the default key. Make sure the POP API License Key credential in n8n is the default API key from popapi.io → Account → API → API Key (default), not the Shopify-specific key — using the wrong key causes an auth_error (HMAC mismatch).


Direct API / Postman

Use Path B above. Send the request to /wp-json/api/v2/connector/zoho with the X-Api-Key header set to your default license key.

A Postman collection with ready-to-use examples (sandbox and live) is available in the examples/ directory of this repository.


LLM / MCP Agents

The connector is fully usable from LLM agents and MCP-based tools. Use Path B (direct /connector/zoho route).

Minimal call sequence for an agent:

  1. Retrieve the webhook ID from the user's POP API account (or have it pre-configured)
  2. Build the data object from the order information available to the agent
  3. POST to /wp-json/api/v2/connector/zoho with environment: "sandbox" first to verify mapping
  4. On success, repeat with environment: "live" to create the real document in Zoho

Use environment: "sandbox" for all test or uncertain calls — no quota is consumed and no document is created in Zoho.


Security Model

Every request from POP API to the n8n connector is protected by two independent verification layers:

1. HMAC-SHA256 (license key binding)

POP API computes HMAC-SHA256(body + timestamp, default_license_key) and sends it in the X-POP-Signature header. The node verifies the signature using the default license key configured in the node parameters. This proves the payload belongs to the customer who owns that account.

POP API always uses the default license key for signing, regardless of which platform (WooCommerce, Shopify, direct API, LLM) triggered the request. Configuring a platform-specific key in the node will cause all requests to fail with auth_error.

2. RSA JWT (POP API origin proof)

POP API signs a short-lived JWT (5 min TTL) with its RSA-2048 private key and injects it as _pop_jwt in the payload. The node verifies the JWT using the public key fetched from your POP API instance. This proves the request originated from POP API servers — a stolen license key alone is not enough to forge requests.

Both checks are always mandatory and cannot be disabled.

Replay Protection

Requests older than 5 minutes are automatically rejected based on the X-POP-Timestamp header.


Error Codes

| Code | Cause | Fix | |------|-------|-----| | auth_error | Invalid HMAC signature, expired JWT, or missing security headers | Make sure the POP API License Key in the node is the default key from popapi.io → Account → API → API Key (default). Platform-specific keys (Shopify, WooCommerce) will not match. Also ensure requests always go through POP API — never call n8n directly. | | config_error | License Key or RSA Public Key not set in node parameters | Configure the node parameters and refresh the public key | | validation_error | Required POP field missing in the payload | Check that data.invoice_body.general_data.*, data.transferee_client.*, and data.order_items are all present | | unsupported_doc_type | doc_type is not TD01 or TD04 | Only TD01 (invoice) and TD04 (credit note) are supported in v1 | | tax_not_found | A tax rate in order_items[].rate is not configured in Zoho | Go to Zoho → Settings → Taxes and add the missing rate | | contact_not_found | Customer not found in Zoho and Create Contact If Missing is disabled | Enable the option or create the contact manually in Zoho | | contact_ambiguous | Multiple Zoho contacts match the same VAT, email, or name | Resolve the duplicate contacts in Zoho before retrying | | contact_creation_failed | Zoho refused the contact creation request | Check the Zoho error details in the n8n execution log | | zoho_api_error | Generic Zoho API error | Check the Zoho error details in the n8n execution log |


Sandbox / Dry Run Mode

Two independent mechanisms to avoid creating real documents during testing:

  • environment: "sandbox" in the POP API request body — POP API injects _pop_dry_run: true into the payload (HMAC-signed, cannot be spoofed). No quota is deducted. Use this for end-to-end testing from any consumer (WooCommerce, Shopify, Postman, LLM).
  • Dry Run parameter on the node — skips Zoho API calls regardless of the _pop_dry_run flag. Useful for local development without Zoho credentials.

When dry run is active, the response includes dry_run: true and the full zoho_body that would have been sent to Zoho, allowing you to verify the field mapping before going live.


Contact Resolution

The node searches for an existing Zoho contact before creating a new one, using the strategy configured in Contact Match Strategy:

  1. VAT → Email → Name (default): searches by VAT/TRN code first, then email address, then company/person name
  2. Email → Name: skips the VAT lookup (useful if VAT codes are not stored in Zoho)

If multiple contacts match, the node returns a contact_ambiguous error — resolve the duplicate in Zoho and retry.

If no contact is found and Create Contact If Missing is enabled, the node creates a new contact using the transferee_client data from the payload, including billing address and VAT treatment for UAE/GCC accounts.


POP Payload Field Mapping

| POP field | Zoho field | |-----------|-----------| | invoice_body.general_data.date | date | | invoice_body.general_data.currency | currency_code | | invoice_body.general_data.invoice_number | (informational — Zoho auto-assigns) | | transferee_client.* | contact lookup / creation | | order_items[].description | line_items[].name | | order_items[].unit_price | line_items[].rate | | order_items[].quantity | line_items[].quantity | | order_items[].rate | tax lookup by percentage → line_items[].tax_id | | order_items[].discount_percent | line_items[].discount (percentage only) | | purchase_order_data.id | reference_number | | payment_data.terms_payment | payment_terms (days) | | connected_invoice_data.id | reference_invoice_id (TD04 only) |


Known Limitations (v1)

  • Line discount precision — Zoho line items only accept percentage discounts. When the POP payload provides an absolute discount_amount, the node converts it to a percentage using discount_amount / unit_price × 100. Rounding may produce a small discrepancy (< 0.01%) on the Zoho total.
  • Contact updates — when an existing contact is found in Zoho, its fields are not updated even if they differ from the POP payload. Update the contact manually in Zoho if needed.

Development

npm install          # install dependencies
npx tsc --noEmit     # type check only
npm run build        # compile to dist/
npm run dev          # watch mode

License

MIT — see LICENSE


Related