Sign In

@getpopapi/n8n-nodes-pop-zoho

Package Overview
Dependencies
Maintainers
2
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@getpopapi/n8n-nodes-pop-zoho

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

latest
Source
npmnpm
Version
0.1.1
Version published
Weekly downloads
4
-33.33%
Maintainers
2
Weekly downloads
 
Created
Source

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

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

  • Go to the Zoho API Console for your region
  • Create a new Server-based Application
  • Set the redirect URI to your n8n OAuth callback URL: https://<your-n8n-instance>/rest/oauth2-credential/callback
  • 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:

FieldDescription
ProductZoho Invoice or Zoho Books — use Books for UAE e-invoicing
RegionYour Zoho data center (EU, US, IN, AU, JP, CA)
Organization IDFrom Zoho → Settings → Organization Profile
Authorization URLAuto-filled based on region — e.g. https://accounts.zoho.eu/oauth/v2/auth
Access Token URLSame host as Authorization URL, path /oauth/v2/token
Client IDFrom your Zoho API Console application
Client SecretFrom your Zoho API Console application
ScopePre-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

[Webhook node]
    ↓
[POP → Zoho node]
    ↓
[Respond to Webhook node]
  • 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.
  • POP → Zoho node — configure as described below.
  • 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

ParameterDescription
POP API URLBase URL of your POP API instance (default: https://popapi.io). Used to auto-fetch the RSA public key.
POP API License KeyYour default POP API license key. Used to verify the HMAC signature on every incoming request.
POP API RSA Public KeyClick 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

ParameterDefaultDescription
Contact Match StrategyVAT → Email → NameHow to find an existing Zoho contact for the invoice customer.
Create Contact If MissingtrueAutomatically create the contact in Zoho when no match is found.
Invoice StatusDraftStatus to set on the created invoice in Zoho (Draft or Sent).
Send Email to CustomerfalseTrigger Zoho's built-in invoice email to the customer after creation.
Place of SupplyNot specifiedRequired for UAE e-invoicing — select the emirate. Leave empty for standard invoices.
Deferred Payment Terms (days)30Days granted for payment when the payload uses deferred payment terms (TP02).

Other

ParameterDefaultDescription
Dry RunfalseValidate 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:

  • Register the n8n webhook URL in the POP API Webhooks panel
  • Set the Connector field on that webhook entry to n8n Connector — Zoho
  • 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": "cliente@example.com",
        "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": "" }
  }
}
FieldValuesDescription
integration.idyour webhook IDThe webhook ID registered in your POP API account
environmentsandbox / livesandbox runs a dry-run — no Zoho calls, no quota deducted
data.invoice_body.general_data.doc_typeTD01 / TD04Document 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

  • In your POP API account → Webhooks panel, add the n8n webhook URL and set the Connector field to n8n Connector — Zoho.
  • In your WooCommerce numeration settings, select the webhook entry you just created.
  • When an order is processed, POP API automatically calls the connector — no changes needed in WooCommerce.
  • 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

  • Register the n8n webhook URL in the POP API webhook panel and set Connector to n8n Connector — Zoho.
  • In the Shopify app, select that webhook entry in the numeration settings for the relevant document type.
  • 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:

  • Retrieve the webhook ID from the user's POP API account (or have it pre-configured)
  • Build the data object from the order information available to the agent
  • POST to /wp-json/api/v2/connector/zoho with environment: "sandbox" first to verify mapping
  • 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

CodeCauseFix
auth_errorInvalid HMAC signature, expired JWT, or missing security headersMake 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_errorLicense Key or RSA Public Key not set in node parametersConfigure the node parameters and refresh the public key
validation_errorRequired POP field missing in the payloadCheck that data.invoice_body.general_data.*, data.transferee_client.*, and data.order_items are all present
unsupported_doc_typedoc_type is not TD01 or TD04Only TD01 (invoice) and TD04 (credit note) are supported in v1
tax_not_foundA tax rate in order_items[].rate is not configured in ZohoGo to Zoho → Settings → Taxes and add the missing rate
contact_not_foundCustomer not found in Zoho and Create Contact If Missing is disabledEnable the option or create the contact manually in Zoho
contact_ambiguousMultiple Zoho contacts match the same VAT, email, or nameResolve the duplicate contacts in Zoho before retrying
contact_creation_failedZoho refused the contact creation requestCheck the Zoho error details in the n8n execution log
zoho_api_errorGeneric Zoho API errorCheck 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:

  • VAT → Email → Name (default): searches by VAT/TRN code first, then email address, then company/person name
  • 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 fieldZoho field
invoice_body.general_data.datedate
invoice_body.general_data.currencycurrency_code
invoice_body.general_data.invoice_number(informational — Zoho auto-assigns)
transferee_client.*contact lookup / creation
order_items[].descriptionline_items[].name
order_items[].unit_priceline_items[].rate
order_items[].quantityline_items[].quantity
order_items[].ratetax lookup by percentage → line_items[].tax_id
order_items[].discount_percentline_items[].discount (percentage only)
purchase_order_data.idreference_number
payment_data.terms_paymentpayment_terms (days)
connected_invoice_data.idreference_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

Resources

  • n8n-nodes-pop — POP API node for n8n (FatturaPA, Peppol, VAT validation)

License

MIT — Babini Mazzari S.r.l.

Keywords

n8n-community-node-package

FAQs

Package last updated on 26 Jun 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts