
Product
Socket Now Protects the Firefox Extension Ecosystem
Socket is bringing experimental protection to Firefox, scanning 97,000+ extensions in Mozilla's official directory for malware and risky updates.
@getpopapi/n8n-nodes-pop-zoho
Advanced tools
n8n custom node — maps POP API payload to Zoho Invoice / Zoho Books
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)
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):
/connector/zoho route — used for Postman, direct API calls, and LLM/MCP agentsIn your n8n instance go to Settings → Community Nodes → Install and enter:
n8n-nodes-pop-zoho
# 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
https://<your-n8n-instance>/rest/oauth2-credential/callback
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.
[Webhook node]
↓
[POP → Zoho node]
↓
[Respond to Webhook node]
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.
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/zohoas 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.

| 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).
| 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). |
| 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. |
The connector can be triggered in two distinct ways depending on the client.
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:
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.
/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": "" }
}
}
| 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).
{
"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
}
}
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.
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 Keycredential 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 anauth_error(HMAC mismatch).
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.
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:
data object from the order information available to the agent/wp-json/api/v2/connector/zoho with environment: "sandbox" first to verify mappingenvironment: "live" to create the real document in ZohoUse
environment: "sandbox"for all test or uncertain calls — no quota is consumed and no document is created in Zoho.
Every request from POP API to the n8n connector is protected by two independent verification layers:
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.
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.
Requests older than 5 minutes are automatically rejected based on the X-POP-Timestamp header.
| 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 |
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)._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.
The node searches for an existing Zoho contact before creating a new one, using the strategy configured in Contact Match Strategy:
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 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) |
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.npm install # install dependencies
npx tsc --noEmit # type check only
npm run build # compile to dist/
npm run dev # watch mode
MIT — Babini Mazzari S.r.l.
FAQs
n8n custom node — maps POP API payload to Zoho Invoice / Zoho Books
The npm package @getpopapi/n8n-nodes-pop-zoho receives a total of 4 weekly downloads. As such, @getpopapi/n8n-nodes-pop-zoho popularity was classified as not popular.
We found that @getpopapi/n8n-nodes-pop-zoho demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

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.

Product
Socket is bringing experimental protection to Firefox, scanning 97,000+ extensions in Mozilla's official directory for malware and risky updates.

Research
/Security News
Three compromised Rust crates pulled in a malicious dependency that downloaded and executed cross-platform malware during Cargo builds.

Research
/Security News
Socket uncovered 77 linked Firefox extensions, including 40 that steal wallet secrets or credentials and 37 deceptive sports-score shells.