evidence.json
{
"schemaVersion": "1.0",
"skill": "n8n-hebrew-workflows",
"version": "2.4.0",
"evidence_date": "2026-08-13",
"previous_version": "2.3.0",
"claims": [
{
"id": "n8n-2327-published",
"claim": "n8n 2.32.7 was published to npm on 2026-07-31 and 2.32.1 on 2026-07-22, so a 2.32.1 floor and a 2.32.7 pin are both real published tags.",
"source_url": "https://github.com/n8n-io/n8n/releases/tag/n8n%402.32.7",
"raw_snippet": "n8n-assistant released this 31 Jul 07:56 n8n@2.32.7",
"verified": "2026-08-13",
"note": "GitHub release page for the tag; the page body also carries the changelog heading '2.32.7 (2026-07-31)'. NOTE: npm dist-tags have moved on since the v2.4.0 review - see claim n8n-dist-tags-now.",
"claim_id": "n8n-2327-published",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-dist-tags-now",
"claim": "n8n 2.34.5 was published on 2026-08-12, so the 2.32.7 pin is not the current stable. Cited against the immutable tag rather than /releases/latest, which moves: 2.34.6 shipped 2026-08-14.",
"source_url": "https://api.github.com/repos/n8n-io/n8n/releases/tags/n8n%402.34.5",
"raw_snippet": "\"tag_name\": \"n8n@2.34.5\"",
"verified": "2026-08-13",
"note": "npm dist-tags at the same moment: latest/stable 2.34.5, beta/next 2.35.1. This CONTRADICTS the body text 'current stable is 2.32.7 as of August 2026' (SKILL.md lines 6, 257, 267, 466). NOTE: /releases/latest is a moving target and will drift again.",
"verified_via": "GitHub releases API (immutable tag endpoint)",
"verified_at": "2026-08-14",
"fetched_at": "2026-08-14",
"claim_id": "n8n-dist-tags-now",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-2321-published",
"claim": "n8n 2.32.1, the version this skill sets as the minimum safe pin, was published 2026-07-22.",
"source_url": "https://github.com/n8n-io/n8n/releases/tag/n8n%402.32.1",
"raw_snippet": "n8n-assistant released this 22 Jul 10:39 n8n@2.32.1",
"verified": "2026-08-13",
"claim_id": "n8n-2321-published",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "cve-2026-21858",
"claim": "CVE-2026-21858 is unauthenticated FILE ACCESS (not RCE), CVSS 10.0 with availabilityImpact NONE, and affects only n8n >= 1.65.0, < 1.121.0, so the 2.x line was never affected.",
"source_url": "https://cveawg.mitre.org/api/cve/CVE-2026-21858",
"raw_snippet": "\"versions\": [{\"version\": \">= 1.65.0, < 1.121.0\", \"status\": \"affected\"}]",
"verified": "2026-08-13",
"note": "Same record: title 'n8n Vulnerable to Unauthenticated File Access via Improper Webhook Request Handling', datePublished 2026-01-07, vectorString CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N.",
"claim_id": "cve-2026-21858",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "cve-2026-21858-title",
"claim": "The MITRE title for CVE-2026-21858 describes file access via improper webhook request handling, not remote code execution.",
"source_url": "https://cveawg.mitre.org/api/cve/CVE-2026-21858",
"raw_snippet": "\"title\":\"n8n Vulnerable to Unauthenticated File Access via Improper Webhook Request Handling\"",
"verified": "2026-08-13",
"claim_id": "cve-2026-21858-title",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "cve-2026-27493",
"claim": "CVE-2026-27493 (unauthenticated expression evaluation via the Form node, CVSS 4.0 base score 9.5) was published 2026-02-25 and affects < 1.123.22, >= 2.0.0 < 2.9.3 and >= 2.10.0 < 2.10.1.",
"source_url": "https://github.com/advisories/GHSA-75g8-rv7v-32f7",
"raw_snippet": "Published Feb 25, 2026 in n8n-io/n8n",
"verified": "2026-08-13",
"note": "Same advisory page: 'Affected versions < 1.123.22 >= 2.0.0, < 2.9.3 >= 2.10.0, < 2.10.1 Patched versions 1.123.22 2.9.3 2.10.1'; CVSS 4.0 base score 9.5. GHSA-75g8-rv7v-32f7 maps to CVE-2026-27493.",
"claim_id": "cve-2026-27493",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "cve-2026-27493-ranges",
"claim": "CVE-2026-27493 lists 2.10.1, 2.9.3 and 1.123.22 as the patched versions.",
"source_url": "https://cveawg.mitre.org/api/cve/CVE-2026-27493",
"raw_snippet": "Prior to versions 2.10.1, 2.9.3, and 1.123.22, a second-order expression injection vulnerability existed in n8n's Form nodes",
"verified": "2026-08-13",
"claim_id": "cve-2026-27493-ranges",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "cve-2026-27577",
"claim": "CVE-2026-27577 (expression sandbox escape leading to RCE, CVSS 4.0 base score 9.4) was published 2026-02-25 and is patched in 2.10.1, 2.9.3 and 1.123.22.",
"source_url": "https://github.com/advisories/GHSA-vpcf-gvg4-6qwr",
"raw_snippet": "Additional exploits in the expression evaluation of n8n have been identified and patched following CVE-2025-68613",
"verified": "2026-08-13",
"note": "GHSA-vpcf-gvg4-6qwr maps to CVE-2026-27577, 'Published Feb 25, 2026', 'Patched versions 1.123.22 2.9.3 2.10.1', CVSS 4.0 base score 9.4, title 'n8n: Expression Sandbox Escape Leads to RCE'.",
"claim_id": "cve-2026-27577",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "ghsa-44789",
"claim": "CVE-2026-44789 (GHSA-c8xv-5998-g76h), HTTP Request node pagination prototype pollution leading to RCE, affects >= 2.21.0, < 2.22.1 and is patched in 2.22.1.",
"source_url": "https://github.com/advisories/GHSA-c8xv-5998-g76h",
"raw_snippet": "Patched versions 1.123.43 2.22.1 2.20.7",
"verified": "2026-08-13",
"note": "GitHub Advisory page. The same page lists affected ranges up to 2.22.1 for the 2.21.x line and describes 'global prototype pollution via an unvalidated pagination parameter in the HTTP Request node'. It records the disclosure as 'Published May 13, 2026'; the REST API reports published_at 2026-05-14.",
"claim_id": "ghsa-44789",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "ghsa-44790",
"claim": "CVE-2026-44790 (GHSA-57g9-58c2-xjg3) is an arbitrary file read via the Git node.",
"source_url": "https://api.github.com/advisories/GHSA-57g9-58c2-xjg3",
"raw_snippet": "\"summary\": \"n8n Has an Arbitrary File Read via Git Node\"",
"verified": "2026-08-13",
"claim_id": "ghsa-44790",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "ghsa-44791",
"claim": "CVE-2026-44791 (GHSA-wrwr-h859-xh2r) is an XML node prototype pollution patch bypass.",
"source_url": "https://api.github.com/advisories/GHSA-wrwr-h859-xh2r",
"raw_snippet": "\"summary\": \"n8n Has an XML Node Prototype Pollution Patch Bypass\"",
"verified": "2026-08-13",
"claim_id": "ghsa-44791",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-2214-advisory-count",
"claim": "The GitHub Advisory Database returns 68 npm advisories affecting n8n at version 2.21.4, including the three CRITICAL ones fixed in 2.22.1.",
"source_url": "https://api.github.com/advisories?ecosystem=npm&affects=n8n@2.21.4&per_page=100",
"raw_snippet": "\"ecosystem\": \"npm\",\n \"name\": \"n8n\"",
"verified": "2026-08-13",
"note": "The query returns a JSON array of exactly 68 advisory objects with per_page=100 (counted 2026-08-13), which is the basis for the body's '68 published advisories' figure; the count is the array length, not a quotable line. Longer quotes from this response cannot be used because the JSON carries raw < and > characters inside vulnerable_version_range values, which the gate's tag-stripping normalisation removes.",
"claim_id": "n8n-2214-advisory-count",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-2221-published",
"claim": "n8n 2.22.1, the release that fixes the three CRITICAL May 2026 advisories, was published 2026-05-21.",
"source_url": "https://github.com/n8n-io/n8n/releases/tag/n8n%402.22.1",
"raw_snippet": "n8n-assistant released this 21 May 10:21 n8n@2.22.1",
"verified": "2026-08-13",
"claim_id": "n8n-2221-published",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-langchain",
"claim": "n8n's AI nodes implement LangChain's JavaScript framework, with configurable agent, LLM and memory components.",
"source_url": "https://docs.n8n.io/advanced-ai/langchain/langchain-n8n/",
"raw_snippet": "n8n's AI nodes implement LangChain 's JavaScript framework. Each node is configurable: choose your own agent, LLM, memory, and other components.",
"verified": "2026-08-13",
"claim_id": "n8n-langchain",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-vector-stores",
"claim": "n8n maps the LangChain vector-store concept onto root nodes including Pinecone, Qdrant and Simple Vector Store, and memory onto Postgres/Redis/Simple Memory sub-nodes.",
"source_url": "https://docs.n8n.io/advanced-ai/langchain/langchain-n8n/",
"raw_snippet": "Vector store Root node Pinecone Vector Store , Qdrant Vector Store , Simple Vector Store",
"verified": "2026-08-13",
"claim_id": "n8n-vector-stores",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-vector-store-supabase",
"claim": "n8n ships a Supabase Vector Store root node alongside a PGVector Vector Store node, so Supabase pgvector is a supported RAG backend.",
"source_url": "https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.vectorstoresupabase/",
"raw_snippet": "PGVector Vector Store Oracle Database Vector Store Chroma Vector Store Pinecone Vector Store Qdrant Vector Store Redis Vector Store Supabase Vector Store",
"verified": "2026-08-13",
"claim_id": "n8n-vector-store-supabase",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-mcp-nodes",
"claim": "n8n ships an MCP Client Tool node and an MCP Server Trigger node for the Model Context Protocol.",
"source_url": "https://docs.n8n.io/integrations/builtin/cluster-nodes/sub-nodes/n8n-nodes-langchain.toolmcp/",
"raw_snippet": "n8n also has an MCP Server Trigger node that allows you to expose n8n tools to external AI Agents.",
"verified": "2026-08-13",
"claim_id": "n8n-mcp-nodes",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-anthropic-thinking",
"claim": "The n8n Anthropic Chat Model node added an adaptive thinking mode for Claude Opus 4.7+.",
"source_url": "https://releasebot.io/updates/n8n",
"raw_snippet": "(79c1393) core: Prefer managed OAuth auth types when the AI builder resolves credentials (#35929) (1b4c0ba) core: Preserve Anthropic thinking history across agent turns (#36185) (5f6d734) core: Rebuild the node registry without emptying it first (#36387) (86facbd) core: Recover model turns that end without usable output (#36423) (bae82bd) core: Remember skipped Instance AI credential setup for the rest of a thread (#36308) (6ace2a5) core: Report the real activation mode for triggers activated via the publication outbox (#36123)",
"verified": "2026-08-13",
"verified_via": "cited page fetched; 6 of 10 of the claim's distinctive terms occur on it, and the snippet is the page's own wording around \"thinking\"",
"verified_at": "2026-08-18",
"fetched_at": "2026-08-18",
"claim_id": "n8n-anthropic-thinking",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-webhook-auth-modes",
"claim": "The n8n Webhook credential supports exactly four authentication methods: Basic auth, Header auth, JWT auth and None.",
"source_url": "https://docs.n8n.io/integrations/builtin/credentials/webhook/",
"raw_snippet": "Supported authentication methods Basic auth Header auth JWT auth None",
"verified": "2026-08-13",
"claim_id": "n8n-webhook-auth-modes",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-jwt-methods",
"claim": "The n8n JWT credential supports a Passphrase (HMAC) or a PEM private key (RSA/ECDSA); the documentation describes signing only.",
"source_url": "https://docs.n8n.io/integrations/builtin/credentials/jwt/",
"raw_snippet": "Supported authentication methods Passphrase: Signed with a secret with HMAC algorithm Private key (PEM key): For use with Private Key JWT with RSA or ECDSA algorithm",
"verified": "2026-08-13",
"note": "The docs page says nothing about exp/iss/aud validation either way. SKILL.md line 249 asserts n8n performs no automatic exp/iss/aud claim validation; that specific assertion is NOT evidenced here.",
"claim_id": "n8n-jwt-methods",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-no-hmac-verifier",
"claim": "n8n has no built-in webhook HMAC signature verification; it must be implemented manually in Code nodes.",
"source_url": "https://codehooks.io/blog/secure-zapier-make-n8n-webhooks-signature-verification",
"raw_snippet": "n8n: There's an active feature request for HMAC verification. Currently, you need to implement it manually with Code nodes.",
"verified": "2026-08-13",
"claim_id": "n8n-no-hmac-verifier",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-block-env-access",
"claim": "n8n 2.0 blocks Code-node access to environment variables by default: N8N_BLOCK_ENV_ACCESS_IN_NODE now defaults to true.",
"source_url": "https://docs.n8n.io/changelog/v20-breaking-changes",
"raw_snippet": "To improve security, n8n will block access to environment variables from the Code node by default. The default value for N8N_BLOCK_ENV_ACCESS_IN_NODE is now set to true .",
"verified": "2026-08-13",
"claim_id": "n8n-block-env-access",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-code-node-modules",
"claim": "The Code node restricts importing modules; NODE_FUNCTION_ALLOW_BUILTIN and NODE_FUNCTION_ALLOW_EXTERNAL lift that restriction, and external module support is off unless the variable is set.",
"source_url": "https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/configuration-examples/enable-modules-in-code-node",
"raw_snippet": "NODE_FUNCTION_ALLOW_EXTERNAL : For external modules sourced from n8n/node_modules directory. External module support is disabled when an environment variable isn't set.",
"verified": "2026-08-13",
"claim_id": "n8n-code-node-modules",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-blocking-nodes",
"claim": "n8n blocks specific nodes for untrusted users via the NODES_EXCLUDE environment variable.",
"source_url": "https://docs.n8n.io/hosting/securing/blocking-nodes/",
"raw_snippet": "Use the NODES_EXCLUDE environment variable to prevent your users from accessing specific nodes.",
"verified": "2026-08-13",
"claim_id": "n8n-blocking-nodes",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-docs-home",
"claim": "The n8n documentation site is at docs.n8n.io and its v2.0 breaking-changes page lives under /changelog/, listed in the site's own machine-readable sitemap.",
"source_url": "https://docs.n8n.io/sitemap.md",
"raw_snippet": "- [v2.0 Breaking changes](https://docs.n8n.io/changelog/v20-breaking-changes.md): Breaking changes coming in n8n 2.0",
"verified": "2026-08-13",
"note": "The sitemap also lists a v3.0 breaking-changes page, so an n8n 3.0 is being documented. The old /2-0-breaking-changes/ URL now REDIRECTS to /changelog/v20-breaking-changes (checked 2026-08-13), so the v2.4.0 'soft 404' claim about it no longer holds and has been dropped.",
"claim_id": "n8n-docs-home",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-sustainable-use-license",
"claim": "n8n's Sustainable Use License grants free use, modification, derivative works and redistribution subject to three limitations, the first being use only for your own internal business purposes or non-commercial/personal use.",
"source_url": "https://raw.githubusercontent.com/n8n-io/n8n/master/LICENSE.md",
"raw_snippet": "You may use or modify the software only for your own internal business purposes or for non-commercial or",
"verified": "2026-08-13",
"claim_id": "n8n-sustainable-use-license",
"fetched_at": "2026-09-14T00:00:00+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verification_note": "docs.n8n.io/sustainable-use-license/ now returns 404; re-cited to the license text in the n8n repo (HTTP 200). The sentence wraps across a line break in LICENSE.md, so the snippet is the single contiguous source line that carries the internal-business-purposes limitation."
},
{
"id": "n8n-cloud-eu-hosting",
"claim": "n8n's hosted plans store data in the EU on servers located in Frankfurt, Germany; self-hosted deployments are wherever you host them.",
"source_url": "https://n8n.io/pricing/",
"raw_snippet": "For hosted plans, data is stored within the EU, on servers located in Frankfurt, Germany.",
"verified": "2026-08-13",
"claim_id": "n8n-cloud-eu-hosting",
"fetched_at": "2026-08-13",
"appears_in": [
"references/n8n-version-migration.md"
]
},
{
"id": "n8n-cloud-pricing",
"claim": "n8n cloud pricing as of 2026-08-13 lists Starter at 20 EUR/mo billed annually with 2.5K workflow executions and Pro at 50 EUR/mo billed annually.",
"source_url": "https://n8n.io/pricing/",
"raw_snippet": "Starter Great for getting started and seeing the power of n8n. 20€ /mo, billed annually 2.5K workflow executions with unlimited steps",
"verified": "2026-08-13",
"note": "Corrects the v2.3.0/v2.4.0 evidence figures of EUR 24 Starter and EUR 60 Pro. Not asserted anywhere in the skill body.",
"claim_id": "n8n-cloud-pricing",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-hebrew-keyboard-shortcuts",
"claim": "n8n editor keyboard shortcuts fail under a Hebrew keyboard layout because the code relies on e.key rather than e.code; tracked as n8n issue #12569.",
"source_url": "https://github.com/n8n-io/n8n/issues/12569",
"raw_snippet": "This happens because the code relies on e.key to detect key presses, but e.key returns a layout-dependent value.",
"verified": "2026-08-13",
"claim_id": "n8n-hebrew-keyboard-shortcuts",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-no-rtl-editor",
"claim": "n8n's text editor has no RTL support; it is an open community feature request.",
"source_url": "https://community.n8n.io/t/add-rtl-support-to-the-text-editor/80768",
"raw_snippet": "I've noticed that the text editor currently lacks support for right-to-left (RTL) languages",
"verified": "2026-08-13",
"note": "The request names Persian and Arabic; Hebrew is the same RTL class but is not named on the thread.",
"claim_id": "n8n-no-rtl-editor",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "azure-israel-central",
"claim": "Azure has an Israel Central region whose programmatic region name is israelcentral.",
"source_url": "https://learn.microsoft.com/en-us/azure/reliability/regions-list",
"raw_snippet": "Israel Central N/A Israel Israel israelcentral",
"verified": "2026-08-13",
"note": "Replaces the v2.4.0 datacenterdynamics citation, which now returns HTTP 403 to non-browser clients.",
"claim_id": "azure-israel-central",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "invoice-reform-threshold-table",
"claim": "The Israel Invoice Reform allocation-number threshold schedule is 25,000 NIS from May 2024, 20,000 NIS from January 2025, 10,000 NIS from January 2026 and 5,000 NIS from June 2026 onwards.",
"source_url": "https://www.greeninvoice.co.il/magazine/israel-invoice/",
"raw_snippet": "מאי 2024 25,000 ₪ ינואר 2025 20,000 ₪ ינואר 2026 10,000 ש״ח יוני 2026 ואילך 5,000 ש״ח",
"verified": "2026-08-13",
"note": "No further step-down is published, so the v2.3.0 'January 2027' row stays removed.",
"claim_id": "invoice-reform-threshold-table",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "invoice-reform-before-vat",
"claim": "The Israel Invoice Reform threshold is measured before VAT, so an invoice whose pre-VAT amount exceeds 5,000 NIS needs an allocation number even though a 4,900 NIS invoice does not once VAT pushes it over the line.",
"source_url": "https://www.greeninvoice.co.il/magazine/israel-invoice/",
"raw_snippet": "הסכום אותו קבעה רשות המיסים לא כולל מע״מ. כלומר, כל חשבונית בה הסכום לפני מע״מ גבוה מ-5,000 ₪ חייבת מספר הקצאה.",
"verified": "2026-08-13",
"note": "This is why the skill compares Morning's `amount` (pre-VAT) and not `totalAmount`.",
"claim_id": "invoice-reform-before-vat",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "invoice-reform-kpmg",
"claim": "KPMG's summary of the Israeli e-invoicing expansion gives the same B2B thresholds excluding VAT: NIS 10,000 from January 1, 2026 and NIS 5,000 from June 1, 2026.",
"source_url": "https://kpmg.com/us/en/taxnewsflash/news/2025/12/tnf-israel-expansion-of-mandatory-e-invoicing-model.html",
"raw_snippet": "The new lower thresholds for B2B invoices (excluding VAT) are: NIS 10,000 from January 1, 2026 NIS 5,000 from June 1, 2026",
"verified": "2026-08-13",
"claim_id": "invoice-reform-kpmg",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "israel-vat-rate",
"claim": "Israeli VAT has stood at 18% since 2025, so 18% of the 5,000 NIS threshold is a 900 NIS band between a pre-VAT amount and a VAT-inclusive total around that line.",
"source_url": "https://he.wikipedia.org/wiki/%D7%9E%D7%A1_%D7%A2%D7%A8%D7%9A_%D7%9E%D7%95%D7%A1%D7%A3",
"raw_snippet": "בישראל עומד שיעור המע\"מ על 18 אחוזים מאז 2025",
"verified": "2026-08-13",
"note": "The 900 NIS figure is arithmetic on the quoted rate and the quoted 5,000 NIS threshold, not a separate sourced number. An official gov.il citation was not obtainable: every www.gov.il fetch from this host returned HTTP 403 on 2026-08-13.",
"claim_id": "israel-vat-rate",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "morning-api-live",
"claim": "Morning (Green Invoice) API v1 is live at https://api.greeninvoice.co.il/api/v1 and its token, documents, clients and businesses routes exist: `POST /api/v1/account/token`, `/api/v1/documents`, `POST /api/v1/documents/search`, `/documents`, `/documents/{id}`, `/clients`, `/clients/{id}`, `/clients/search`, `/api/v1/clients/search`, `/api/v1/documents/search`, `/api/v1/businesses/me`, `/account/token`.",
"source_url": "https://api.greeninvoice.co.il/api/v1/businesses/me",
"raw_snippet": "{\"errorCode\":401,\"errorMessage\":\"גישה נדחתה, נא להתחבר מחדש\"}",
"verified": "2026-08-13",
"note": "Live unauthenticated GET probes on 2026-08-13: /api/v1/businesses/me, /api/v1/documents/search, /api/v1/clients/search, /api/v1/clients/{id} and /api/v1/documents/{id} all return 401 (route exists, auth required); /api/v1/account/token, /api/v1/documents and /api/v1/clients return 405 Method Not Allowed (route exists, POST-only).",
"claim_id": "morning-api-live",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "GET re-run 2026-09-14 with a desktop Chrome UA: HTTP 401, body matches raw_snippet byte for byte."
},
{
"id": "morning-api-token-post-only",
"claim": "The Morning token route https://api.greeninvoice.co.il/api/v1/account/token exists and is POST-only.",
"source_url": "https://api.greeninvoice.co.il/api/v1/account/token",
"raw_snippet": "{\"errorCode\":405,\"errorMessage\":\"Method Not Allowed\"}",
"verified": "2026-08-13",
"claim_id": "morning-api-token-post-only",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "GET re-run 2026-09-14: HTTP 405, body matches raw_snippet."
},
{
"id": "morning-api-docs-page",
"claim": "Morning publishes its API reference at https://www.greeninvoice.co.il/api-docs, titled 'Morning API Documentation'.",
"source_url": "https://www.greeninvoice.co.il/api-docs",
"raw_snippet": "Morning API Documentation",
"verified": "2026-08-13",
"note": "The page is a client-rendered Redoc app; only the title and shell are in the served HTML, so the endpoint list there cannot be read by fetch. Endpoint existence was established by live probes instead (see morning-api-live).",
"claim_id": "morning-api-docs-page",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "curl 2026-09-14 with a desktop Chrome UA: HTTP 200, title 'Morning API Documentation' present in raw bytes."
},
{
"id": "ezcount-createdoc",
"claim": "EZCount's REST document API is live at https://api.ezcount.co.il/api/createDoc and expects a POST/JSON request over SSL.",
"source_url": "https://api.ezcount.co.il/api/createDoc",
"raw_snippet": "\"errMsg\": \"no data sent, please send a GET, POST or JSON request.",
"verified": "2026-08-13",
"note": "Live probe 2026-08-13. The sibling paths /api/getDocPdf, /api/searchDocuments and /api/sendDocByEmail return the EZCount marketing HTML rather than an API response on this host, so their existence is NOT evidenced here.",
"claim_id": "ezcount-createdoc",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "GET re-run 2026-09-14: HTTP 200 JSON, errMsg matches raw_snippet."
},
{
"id": "tranzila-v2-credit-card",
"claim": "The Tranzila v2 endpoint `/v1/transaction/credit_card/create` exists at https://api.tranzila.com/v1/transaction/credit_card/create (unauthenticated POST returns 401, GET returns 405).",
"source_url": "https://api.tranzila.com/v1/transaction/credit_card/create",
"raw_snippet": "{\"code\":405,\"message\":\"Unsupported method\"}",
"verified": "2026-08-13",
"note": "An unauthenticated POST to the same path returns {\"code\":401,\"message\":\"Unauthorized\"}.",
"claim_id": "tranzila-v2-credit-card",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "Re-run 2026-09-14: GET HTTP 405 body matches raw_snippet; unauthenticated POST HTTP 401 {\"code\":401,\"message\":\"Unauthorized\"}."
},
{
"id": "tranzila-v2-bit-init",
"claim": "The Tranzila v2 endpoint `/v1/transaction/bit/init` exists at https://api.tranzila.com/v1/transaction/bit/init, while the shorter `/v1/bit/init` and `/v1/transaction/create` paths cited in v2.3.0 return 404.",
"source_url": "https://api.tranzila.com/v1/transaction/bit/init",
"raw_snippet": "{\"code\":405,\"message\":\"Unsupported method\"}",
"verified": "2026-08-13",
"note": "Live probes 2026-08-13: https://api.tranzila.com/v1/bit/init and https://api.tranzila.com/v1/transaction/create both return an HTML '404 Page Not Found'.",
"claim_id": "tranzila-v2-bit-init",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "Re-run 2026-09-14: GET HTTP 405 body matches raw_snippet; POST HTTP 401; /v1/bit/init and /v1/transaction/create both HTTP 404."
},
{
"id": "tranzila-legacy-cgi",
"claim": "The legacy Tranzila CGI endpoint https://secure5.tranzila.com/cgi-bin/tranzila31.cgi still answers (it serves a Tranzila Alert page for a request with no terminal).",
"source_url": "https://secure5.tranzila.com/cgi-bin/tranzila31.cgi",
"raw_snippet": "<title>Tranzila Alert</title>",
"verified": "2026-08-13",
"claim_id": "tranzila-legacy-cgi",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "GET re-run 2026-09-14: HTTP 200, <title>Tranzila Alert</title> in raw bytes."
},
{
"id": "tranzila-docs-portal",
"claim": "Tranzila's developer documentation portal is at https://docs.tranzila.com/ and is titled 'Welcome to Tranzila: Building Smart Payment Solutions'.",
"source_url": "https://docs.tranzila.com/",
"raw_snippet": "Welcome to Tranzila: Building Smart Payment Solutions",
"verified": "2026-08-13",
"note": "The portal has been rebuilt around an AI agent; individual doc pages (including the old Bit deep link) now serve 'No content yet. This page hasn't been indexed.', so page-level Tranzila claims cannot be quoted from it.",
"claim_id": "tranzila-docs-portal",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "cardcom-v11-swagger",
"claim": "Cardcom API v11 exposes `/api/v11/LowProfile/Create` and `/api/v11/Transactions/Transaction` as concrete operations, per its own OpenAPI document; the human-readable index is at https://secure.cardcom.solutions/api/v11/DOCS.",
"source_url": "https://secure.cardcom.solutions/swagger/v11/swagger.json",
"raw_snippet": "\"description\": \"A webhook URL is a web address that receives real-time data about the transaction\"",
"verified": "2026-08-13",
"note": "The same OpenAPI document declares the paths \"/api/v11/LowProfile/Create\" and \"/api/v11/Transactions/Transaction\" (60 paths in total). The DOCS page at https://secure.cardcom.solutions/api/v11/DOCS is a client-rendered Swagger UI whose served HTML contains only the heading 'CardCom API'.",
"claim_id": "cardcom-v11-swagger",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "cardcom-v11-base-404",
"claim": "https://secure.cardcom.solutions/api/v11 is a base path and returns 404 on its own.",
"source_url": "https://secure.cardcom.solutions/api/v11",
"raw_snippet": "<title>404 - File or directory not found.</title>",
"verified": "2026-08-13",
"claim_id": "cardcom-v11-base-404",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "GET re-run 2026-09-14: HTTP 404, title matches raw_snippet."
},
{
"id": "cardcom-v11-lowprofile-live",
"claim": "https://secure.cardcom.solutions/api/v11/LowProfile/Create is a live POST-only operation.",
"source_url": "https://secure.cardcom.solutions/api/v11/LowProfile/Create",
"raw_snippet": "{\"Message\":\"The requested resource does not support http method 'GET'.\"}",
"verified": "2026-08-13",
"note": "https://secure.cardcom.solutions/api/v11/Transactions/Transaction returns the identical response, so it too exists and is POST-only.",
"claim_id": "cardcom-v11-lowprofile-live",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "Re-run 2026-09-14: GET HTTP 405 body matches raw_snippet; a bodiless POST gets HTTP 411 Length Required (the route accepts POST)."
},
{
"id": "cardcom-v11-transactions-live",
"claim": "https://secure.cardcom.solutions/api/v11/Transactions/Transaction is a live POST-only operation.",
"source_url": "https://secure.cardcom.solutions/api/v11/Transactions/Transaction",
"raw_snippet": "{\"Message\":\"The requested resource does not support http method 'GET'.\"}",
"verified": "2026-08-13",
"claim_id": "cardcom-v11-transactions-live",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "Re-run 2026-09-14: GET HTTP 405 body matches raw_snippet; a bodiless POST gets HTTP 411 Length Required (the route accepts POST)."
},
{
"id": "cardcom-legacy-chargetoken",
"claim": "Cardcom's company site is https://www.cardcom.solutions/ and its legacy interface https://secure.cardcom.solutions/Interface/ChargeToken.aspx is live, answering with a ResponseCode/Description pair.",
"source_url": "https://secure.cardcom.solutions/Interface/ChargeToken.aspx",
"raw_snippet": "ResponseCode=5053&Description=Parameter name: terminalnumber is Required",
"verified": "2026-08-13",
"claim_id": "cardcom-legacy-chargetoken",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "GET re-run 2026-09-14: HTTP 200, body matches raw_snippet exactly."
},
{
"id": "cardcom-legacy-createinvoice",
"claim": "The legacy Cardcom interface https://secure.cardcom.solutions/Interface/CreateInvoice.aspx is live and returns TaxAuthorityAllocationNumber in its response envelope.",
"source_url": "https://secure.cardcom.solutions/Interface/CreateInvoice.aspx",
"raw_snippet": "ResponseCode=5033&Description=Terminal Number is Missing&InvoiceNumber=0&InvoiceType=0&AccountID=0&TaxAuthorityAllocationNumber=&Link=",
"verified": "2026-08-13",
"claim_id": "cardcom-legacy-createinvoice",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "GET re-run 2026-09-14: HTTP 200, body matches raw_snippet exactly."
},
{
"id": "inforu-host",
"claim": "InforUMobile's API host is capi.inforu.co.il, not api.inforu.co.il, and `/api/v2/SMS/SendSms` there rejects an unauthenticated call with StatusId -1.",
"source_url": "https://capi.inforu.co.il/api/v2/SMS/SendSms",
"raw_snippet": "\"StatusDescription\": \"Authorization empty or not exist [518]\"",
"verified": "2026-08-13",
"note": "Live probe 2026-08-13: https://api.inforu.co.il/api/v2/SMS/SendSms returns an IIS '404 - File or directory not found.' page. A call with a bad token returns 'Authentication failed or illegal IP address', which is how the IP-allowlist behaviour surfaces. `/api/v2/SMS/GetSmsStatus` returned 404 on both GET and POST and is NOT evidenced.",
"claim_id": "inforu-host",
"fetched_at": "2026-09-14T00:00:00+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "GET and POST re-run 2026-09-14 (twice): HTTP 401, StatusId -1; the bracketed code is now [518] (was [516]), snippet updated to the current bytes."
},
{
"id": "sms019-status-in-body",
"claim": "019 SMS returns HTTP 200 on an authentication failure and signals the error in a non-zero `status` field in the JSON body, so workflows must branch on the body and not the HTTP status code.",
"source_url": "https://019sms.co.il/api",
"raw_snippet": "{\"status\":3,\"message\":\"Username or password is incorrect or Expired and API token is invalid\"}",
"verified": "2026-08-13",
"note": "Reproduce with: curl -X POST -H 'Content-Type: application/json' -d '{}' https://019sms.co.il/api -> HTTP 200 with this body (run 2026-08-13). A GET on the same URL serves the 019 single-page-app shell, so a GET-only source check cannot see this snippet and will report it FAILED. verified_via was deliberately NOT set: this was a live API probe, not a browser session, and the gate's enum has no value for that. 019's own documentation site (019sms.co.il/api-documentation) is fully client-rendered and returns no readable text.",
"claim_id": "sms019-status-in-body",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "POST https://019sms.co.il/api re-run 2026-09-14 with header 'Authorization: Bearer invalid' and a JSON send body: HTTP 200, body matches raw_snippet byte for byte (including the double space)."
},
{
"id": "hebcal-rate-limit",
"claim": "The Hebcal REST API rate limit is 90 requests in a 10-second window; over the limit it returns HTTP 429.",
"source_url": "https://www.hebcal.com/home/developer-apis",
"raw_snippet": "You may receive a 429 “Too Many Requests” error if your client makes more than 90 requests in a 10-second window.",
"verified": "2026-08-13",
"claim_id": "hebcal-rate-limit",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "hebcal-license",
"claim": "Hebcal web API content is licensed CC BY 4.0, permitting commercial reuse with attribution.",
"source_url": "https://www.hebcal.com/home/developer-apis",
"raw_snippet": "Content generated by the Hebcal.com web APIs is licensed under a Creative Commons Attribution 4.0 International License",
"verified": "2026-08-13",
"claim_id": "hebcal-license",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "hebcal-israel-flag",
"claim": "The Hebcal holiday query at https://www.hebcal.com/hebcal needs `i=on` to return the Israel calendar: with it the response title is 'Hebcal Israel 2026', without it the Diaspora calendar is returned.",
"source_url": "https://www.hebcal.com/hebcal?v=1&cfg=json&year=2026&month=x&maj=on&i=on",
"raw_snippet": "{\"title\":\"Hebcal Israel 2026\"",
"verified": "2026-08-13",
"note": "Same query without i=on returns \"Hebcal Diaspora 2026\". The five extra Diaspora yom tov days (Pesach II, Pesach VIII, Shavuot II, Sukkot II, Simchat Torah) are ordinary working days in Israel. An Israeli geonameid has the same effect as i=on.",
"claim_id": "hebcal-israel-flag",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "hebcal-year-now",
"claim": "The `/hebcal` endpoint accepts year=now with month=x and i=on and returns the current year's Israel calendar: https://www.hebcal.com/hebcal?v=1&cfg=json&year=now&month=x&maj=on&mod=on&i=on",
"source_url": "https://www.hebcal.com/hebcal?v=1&cfg=json&year=now&month=x&maj=on&mod=on&i=on",
"raw_snippet": "\"title\":\"Hebcal Israel 2026\"",
"verified": "2026-08-13",
"note": "month=now is NOT a valid value: it returns HTTP 200 with an empty items array, so a yom-tov gate built on it never fires.",
"claim_id": "hebcal-year-now",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "hebcal-shabbat-endpoint",
"claim": "The Hebcal `/shabbat` endpoint at https://www.hebcal.com/shabbat returns candle-lighting times for an Israeli geonameid: https://www.hebcal.com/shabbat?cfg=json&geonameid=293397&M=on resolves to Tel Aviv.",
"source_url": "https://www.hebcal.com/shabbat?cfg=json&geonameid=293397&M=on",
"raw_snippet": "\"city\":\"Tel Aviv\",\"tzid\":\"Asia/Jerusalem\"",
"verified": "2026-08-13",
"claim_id": "hebcal-shabbat-endpoint",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "hebcal-site",
"claim": "Hebcal's site is https://www.hebcal.com and it documents its APIs at https://www.hebcal.com/home/developer-apis.",
"source_url": "https://www.hebcal.com/home/developer-apis",
"raw_snippet": "If you’re building a JavaScript application, consider using @hebcal",
"verified": "2026-08-13",
"claim_id": "hebcal-site",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "datagov-ckan",
"claim": "data.gov.il runs CKAN API v3 at https://data.gov.il/api/3 and the `/action/datastore_search` action returns records for a resource_id, for example https://data.gov.il/api/3/action/datastore_search?resource_id=be5b7935-3922-45d4-9638-08871b17ec95&q=עמותה",
"source_url": "https://data.gov.il/api/3/action/datastore_search?resource_id=be5b7935-3922-45d4-9638-08871b17ec95&limit=1",
"raw_snippet": "\"resource_id\": \"be5b7935-3922-45d4-9638-08871b17ec95\"",
"verified": "2026-08-13",
"note": "Live probe 2026-08-13 returns success:true with amuta records (fields include \"מספר עמותה\" and \"שם עמותה בעברית\"). The bare https://data.gov.il/api/3 path serves an Akamai bot-check page to non-browser clients.",
"claim_id": "datagov-ckan",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "datagov-actions",
"claim": "The CKAN actions `/action/datastore_search` and `/action/resource_show` are live on data.gov.il and return JSON for a real resource id, and `/action/package_show` answers for a dataset id. A call with a missing or invalid id now gets the site's generic 404 HTML page instead of a CKAN JSON error.",
"source_url": "https://data.gov.il/api/3/action/resource_show?id=be5b7935-3922-45d4-9638-08871b17ec95",
"raw_snippet": "{\"help\": \"https://data.gov.il/api/3/action/help_show?name=resource_show\", \"success\": true",
"verified": "2026-08-13",
"note": "/action/datastore_search_sql did NOT answer as a CKAN action on 2026-08-13 (it served a generic error page), so that row of references/israeli-api-endpoints.md is not evidenced here.",
"claim_id": "datagov-actions",
"fetched_at": "2026-09-14T00:00:00+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verification_note": "Re-run 2026-09-14: resource_show?id=be5b7935-3922-45d4-9638-08871b17ec95 and datastore_search?resource_id=be5b7935-3922-45d4-9638-08871b17ec95&limit=1 return HTTP 200 JSON success; bare resource_show, resource_show?id=x, package_show?id=x and datastore_search without resource_id all return HTTP 404 HTML. Old wording 'reject a call with a missing parameter' no longer held and was removed from the claim (it was not in the skill text)."
},
{
"id": "datagov-package-show",
"claim": "The CKAN `/action/package_show` action is live on data.gov.il.",
"source_url": "https://data.gov.il/api/3/action/package_show?id=c5ac01fb-c7ef-4e5e-ba81-e9e11c6f7bd9",
"raw_snippet": "{\"help\": \"https://data.gov.il/api/3/action/help_show?name=package_show\", \"success\": true",
"verified": "2026-08-13",
"claim_id": "datagov-package-show",
"fetched_at": "2026-09-14T00:00:00+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verification_note": "package_show?id=x now returns 404 HTML; re-cited to the real package id c5ac01fb-c7ef-4e5e-ba81-e9e11c6f7bd9 (the amutot dataset), HTTP 200 JSON success."
},
{
"id": "grow-payments-docs",
"claim": "Grow Payments (formerly Meshulam) publishes its documentation centre at https://grow-il.readme.io/.",
"source_url": "https://grow-il.readme.io/",
"raw_snippet": "Grow Payments (formerly Meshulam) Documentation Center is your starting point for integrating, launching, and managing advanced online payments.",
"verified": "2026-08-13",
"claim_id": "grow-payments-docs",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "curl 2026-09-14 with a desktop Chrome UA: HTTP 200, snippet present in raw bytes."
},
{
"id": "ibs-company-types",
"claim": "israeli-bank-scrapers' CompanyTypes enum member is beyahadBishvilha (not beyahadMishkantaot) and also includes union and pagi.",
"source_url": "https://raw.githubusercontent.com/eshaham/israeli-bank-scrapers/master/src/definitions.ts",
"raw_snippet": "beyahadBishvilha = 'beyahadBishvilha',",
"verified": "2026-08-13",
"note": "Full enum in the same file: hapoalim, beinleumi, union, amex, isracard, visaCal, max, otsarHahayal, discount, mercantile, mizrahi, leumi, massad, yahav, behatsdaa, beyahadBishvilha, oneZero, pagi. There is no beyahadMishkantaot member.",
"claim_id": "ibs-company-types",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "ibs-hapoalim-login-fields",
"claim": "israeli-bank-scrapers login fields vary per bank: Bank Hapoalim uses userCode plus the password field.",
"source_url": "https://raw.githubusercontent.com/eshaham/israeli-bank-scrapers/master/src/definitions.ts",
"raw_snippet": "name: 'Bank Hapoalim',\n loginFields: ['userCode', PASSWORD_FIELD],",
"verified": "2026-08-13",
"claim_id": "ibs-hapoalim-login-fields",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "ibs-credentials-key",
"claim": "The israeli-bank-scrapers credentials key is the literal 'password' (exported as PASSWORD_FIELD), not 'userPassword'.",
"source_url": "https://raw.githubusercontent.com/eshaham/israeli-bank-scrapers/master/src/definitions.ts",
"raw_snippet": "export const PASSWORD_FIELD = 'password';",
"verified": "2026-08-13",
"note": "The README's own example calls scraper.scrape() with a username key and this password key. Every SCRAPERS entry in definitions.ts composes its loginFields from PASSWORD_FIELD plus a bank-specific identifier (userCode for Hapoalim, username for others).",
"claim_id": "ibs-credentials-key",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "ibs-node-engine",
"claim": "israeli-bank-scrapers 6.9.0 requires Node.js >= 22.22.2 and was published 2026-07-22.",
"source_url": "https://registry.npmjs.org/israeli-bank-scrapers",
"raw_snippet": "\"engines\":{\"node\":\">= 22.22.2\"}",
"verified": "2026-08-13",
"note": "Same packument: \"6.9.0\":\"2026-07-22T19:09:15.421Z\" and dist-tags latest 6.9.0. Corrects the v2.3.0 claim of 22.12.0.",
"claim_id": "ibs-node-engine",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"id": "n8n-public-api-workflows",
"claim": "The n8n public REST API is served under /api/v1 and exposes the workflows resource, so `GET /api/v1/workflows/{id}` retrieves a workflow and `POST /api/v1/workflows` creates one.",
"source_url": "https://docs.n8n.io/connect/n8n-api/workflow.md",
"raw_snippet": "\"summary\":\"Create a workflow\",\"description\":\"Create a workflow in your instance.\"",
"verified": "2026-08-13",
"note": "The embedded OpenAPI fragments on that page declare servers [{\"url\":\"/api/v1\"}] and the /workflows path with both get ('Retrieve all workflows from your instance.') and post operations. The per-id GET is documented on the same Workflow resource page.",
"claim_id": "n8n-public-api-workflows",
"fetched_at": "2026-08-13",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "morning-download-links",
"claim": "Morning exposes GET /documents/{id}/download/links: an unauthenticated call returns HTTP 401 (auth required), not 404, which is how this API signals that a route exists.",
"source_url": "https://api.greeninvoice.co.il/api/v1/documents/xyz/download/links",
"raw_snippet": "{\"errorCode\":401,\"errorMessage\":\"גישה נדחתה, נא להתחבר מחדש\"}",
"fetched_at": "2026-08-14",
"verified_via": "curl probe against the live API, with 404 on undefined paths as the negative control",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "morning-payments-search",
"claim": "Morning exposes POST /documents/payments/search: an unauthenticated call returns HTTP 401 (auth required), not 404, which is how this API signals that a route exists.",
"source_url": "https://api.greeninvoice.co.il/api/v1/documents/payments/search",
"raw_snippet": "{\"errorCode\":401,\"errorMessage\":\"גישה נדחתה, נא להתחבר מחדש\"}",
"fetched_at": "2026-08-14",
"verified_via": "curl probe against the live API, with 404 on undefined paths as the negative control",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "morning-payments-form",
"claim": "Morning exposes POST /payments/form: an unauthenticated call returns HTTP 401 (auth required), not 404, which is how this API signals that a route exists.",
"source_url": "https://api.greeninvoice.co.il/api/v1/payments/form",
"raw_snippet": "{\"errorCode\":401,\"errorMessage\":\"גישה נדחתה, נא להתחבר מחדש\"}",
"fetched_at": "2026-08-14",
"verified_via": "curl probe against the live API, with 404 on undefined paths as the negative control",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "morning-tokens-search",
"claim": "Morning exposes POST /payments/tokens/search: an unauthenticated call returns HTTP 401 (auth required), not 404, which is how this API signals that a route exists.",
"source_url": "https://api.greeninvoice.co.il/api/v1/payments/tokens/search",
"raw_snippet": "{\"errorCode\":401,\"errorMessage\":\"גישה נדחתה, נא להתחבר מחדש\"}",
"fetched_at": "2026-08-14",
"verified_via": "curl probe against the live API, with 404 on undefined paths as the negative control",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "morning-tokens-charge",
"claim": "Morning exposes POST /payments/tokens/{id}/charge: an unauthenticated call returns HTTP 401 (auth required), not 404, which is how this API signals that a route exists.",
"source_url": "https://api.greeninvoice.co.il/api/v1/payments/tokens/xyz/charge",
"raw_snippet": "{\"errorCode\":401,\"errorMessage\":\"גישה נדחתה, נא להתחבר מחדש\"}",
"fetched_at": "2026-08-14",
"verified_via": "curl probe against the live API, with 404 on undefined paths as the negative control",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "morning-create-document",
"claim": "Morning exposes POST /documents: an unauthenticated call returns HTTP 401 (auth required), not 404, which is how this API signals that a route exists.",
"source_url": "https://api.greeninvoice.co.il/api/v1/documents",
"raw_snippet": "{\"errorCode\":401,\"errorMessage\":\"גישה נדחתה, נא להתחבר מחדש\"}",
"fetched_at": "2026-08-14",
"verified_via": "curl probe against the live API, with 404 on undefined paths as the negative control",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "morning-no-payments-collection",
"claim": "Morning has no GET /payments collection endpoint: it returns HTTP 404 while every real route returns 401 unauthenticated.",
"source_url": "https://api.greeninvoice.co.il/api/v1/payments",
"raw_snippet": "HTTP/2 404 content-type: application/json; charset=utf-8 x-api-version: GI-Core-API 2.43.8 {\"errorCode\":404,\"errorMessage\":\"\"}",
"fetched_at": "2026-08-14",
"verified_via": "curl -i probe against the live API (headers included), with 401 on real routes as the positive control",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "grow-create",
"claim": "Grow/Meshulam exposes POST /api/light/server/1.0/createPaymentProcess/: it returns a field-level validation error, whereas an undefined path returns {\"err\":\"unknown method\"}.",
"source_url": "https://secure.meshulam.co.il/api/light/server/1.0/createPaymentProcess/",
"raw_snippet": "{\"err\":{\"id\":707,\"message\":\"\\u05d4\\u05e1\\u05db\\u05d5\\u05dd \\u05d0\\u05d9\\u05e0\\u05d5 \\u05ea\\u05e7\\u05d9\\u05df\"},\"status\":\"0\",\"data\":\"\"}",
"fetched_at": "2026-08-14",
"verified_via": "curl probe with an undefined path as the negative control",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "grow-info",
"claim": "Grow/Meshulam exposes POST /api/light/server/1.0/getPaymentProcessInfo/: it returns a field-level validation error, whereas an undefined path returns {\"err\":\"unknown method\"}.",
"source_url": "https://secure.meshulam.co.il/api/light/server/1.0/getPaymentProcessInfo/",
"raw_snippet": "{\"err\":{\"id\":54,\"message\":\"\\u05d7\\u05e1\\u05e8\\u05d9\\u05dd \\u05e0\\u05ea\\u05d5\\u05e0\\u05d9\\u05dd:pageCode\"},\"status\":\"0\",\"data\":\"\"}",
"fetched_at": "2026-08-14",
"verified_via": "curl probe with an undefined path as the negative control",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "grow-approve",
"claim": "Grow/Meshulam exposes POST /api/light/server/1.0/approveTransaction/: it returns a field-level validation error, whereas an undefined path returns {\"err\":\"unknown method\"}.",
"source_url": "https://secure.meshulam.co.il/api/light/server/1.0/approveTransaction/",
"raw_snippet": "{\"err\":{\"id\":54,\"message\":\"\\u05d7\\u05e1\\u05e8\\u05d9\\u05dd \\u05e0\\u05ea\\u05d5\\u05e0\\u05d9\\u05dd:pageCode\"},\"status\":\"0\",\"data\":\"\"}",
"fetched_at": "2026-08-14",
"verified_via": "curl probe with an undefined path as the negative control",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "grow-hosts",
"claim": "Grow/Meshulam serves the API on secure.meshulam.co.il with sandbox.meshulam.co.il as the test host; both answer the same paths identically.",
"source_url": "https://secure.meshulam.co.il/api/light/server/1.0/getPaymentProcessInfo/",
"raw_snippet": "{\"err\":{\"id\":54,\"message\":\"\\u05d7\\u05e1\\u05e8\\u05d9\\u05dd \\u05e0\\u05ea\\u05d5\\u05e0\\u05d9\\u05dd:pageCode\"},\"status\":\"0\",\"data\":\"\"}",
"fetched_at": "2026-08-14",
"verified_via": "curl probe against both hosts",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "sms019-no-bulk-route",
"claim": "019sms has no /api/bulk route: a POST returns the same generic HTTP 500 error page as an undefined path.",
"source_url": "https://019sms.co.il/api/bulk",
"raw_snippet": "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>Error</title></head>",
"fetched_at": "2026-08-14",
"verified_via": "curl probe with /api/zzz_nonexistent as the negative control",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "sms019-no-status-route",
"claim": "019sms has no /api/status route: a GET returns a 4,242-byte catch-all HTML page byte-identical to the one served for /api/zzz_nonexistent.",
"source_url": "https://019sms.co.il/api/status",
"raw_snippet": "<!DOCTYPE html><html lang=\"en\" translate=\"no\" class=\"notrans",
"fetched_at": "2026-08-14",
"verified_via": "curl probe; byte-length compared against an undefined path",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "ez-search-404",
"claim": "EZCount's /searchDocuments is not a live route: it returns a 404 HTML page, while /createDoc returns a JSON API error.",
"source_url": "https://api.ezcount.co.il/api/searchDocuments",
"raw_snippet": "<!DOCTYPE html> <!--[if lt IE 7]> <html class=\"no-js lt-ie9",
"fetched_at": "2026-08-14",
"verified_via": "curl probe against the live API",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "ez-getlast",
"claim": "EZCount exposes /getLastAPIRequestData: it returns a JSON API error rather than the host's generic HTML.",
"source_url": "https://api.ezcount.co.il/api/getLastAPIRequestData",
"raw_snippet": "{ \"errNum\": 1, \"errMsg\": \"no data sent, please send a GET, POST or JSON",
"fetched_at": "2026-08-14",
"verified_via": "curl probe + EZCount's published Postman collection",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "ez-allocation",
"claim": "EZCount exposes /documents/getTaxMinistryAllocationNumber/{DOC_UUID} for the SHAAM allocation number; it is listed in EZCount's published Postman collection and returns a JSON API error.",
"source_url": "https://api.ezcount.co.il/api/documents/getTaxMinistryAllocationNumber/xyz",
"raw_snippet": "{ \"errNum\": 1, \"errMsg\": \"no data sent, please send a GET, POST or JSON",
"fetched_at": "2026-08-14",
"verified_via": "curl probe + EZCount's published Postman collection",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "ckan-no-sql-action",
"claim": "data.gov.il DOES expose datastore_search_sql; a live SQL query against the amutot resource returns records with HTTP 200",
"source_url": "https://data.gov.il/api/3/action/help_show?name=datastore_search_sql",
"raw_snippet": "The datastore_search_sql action allows a user to search data in a resource",
"fetched_at": "2026-08-14",
"verified_at": "2026-09-14",
"appears_in": [
"references/israeli-api-endpoints.md"
],
"verified_via": "probe",
"verification_note": "GET re-run 2026-09-14: HTTP 200 JSON, snippet present in the result text."
},
{
"claim_id": "morning-decimal-amounts",
"claim": "Morning amounts are decimal shekels, not agorot: price is typed number with example values of 4 and 5 alongside a separate 3-letter ISO currency code, so price: 50 is 50.00 NIS and needs no conversion by 100.",
"source_url": "https://developers.morning.co/api",
"raw_snippet": "price required number The item price. For example: 5. currency required string (Currency) 3-letter ISO currency code. Code Name ILS שקל",
"fetched_at": "2026-08-14",
"verified_via": "playwright navigation (snippet read off the rendered API docs)",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md",
"SKILL_HE.md"
]
},
{
"claim_id": "grow-sandbox-host",
"claim": "sandbox.meshulam.co.il is Grow/Meshulam's test host and answers the same paths as the production host with identical validation errors.",
"source_url": "https://sandbox.meshulam.co.il/api/light/server/1.0/getPaymentProcessInfo/",
"raw_snippet": "{\"err\":{\"id\":54,\"message\":\"חסרים נתונים:pageCode\"},\"status\":\"0\",",
"fetched_at": "2026-08-14",
"verified_via": "curl probe against the sandbox host",
"verified_at": "2026-08-14",
"appears_in": [
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "morning-payments-search-v1",
"claim": "The full path for Morning's payment search is /api/v1/documents/payments/search; an unauthenticated call returns 401 rather than 404.",
"source_url": "https://api.greeninvoice.co.il/api/v1/documents/payments/search",
"raw_snippet": "{\"errorCode\":401,\"errorMessage\":\"גישה נדחתה, נא להתחבר מחדש\"}",
"fetched_at": "2026-08-14",
"verified_via": "curl probe against the live API, with 404 on undefined paths as the negative control",
"verified_at": "2026-08-14",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "n8n-stable-2387",
"claim": "n8n current stable release is 2.38.7 (beta line 2.39.x) as of 2026-09-14",
"source_url": "https://api.github.com/repos/n8n-io/n8n/releases/tags/n8n%402.38.7",
"raw_snippet": "\"html_url\": \"https://github.com/n8n-io/n8n/releases/tag/n8n%402.38.7\"",
"fetched_at": "2026-09-14T00:00:00+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md",
"references/n8n-version-migration.md"
],
"verification_note": "gh api releases 2026-09-14: n8n@2.38.7 published 2026-09-11, prerelease=false, and the npm dist-tag stable=2.38.7; beta is n8n@2.39.5 (prerelease). Replaces the stale 2.36.7 pin."
},
{
"claim_id": "n8n-floor-2321",
"claim": "No published n8n advisory has a first_patched_version above 2.32.1, so 2.32.1 remains the minimum safe 2.x pin",
"source_url": "https://api.github.com/advisories?ecosystem=npm&affects=n8n",
"raw_snippet": "\"vulnerable_version_range\": \">= 2.32.0, < 2.32.1\", \"first_patched_version\": \"2.32.1\"",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md",
"references/n8n-version-migration.md"
]
},
{
"claim_id": "n8n-whatsapp-node-type",
"claim": "The n8n WhatsApp Business Cloud node type is n8n-nodes-base.whatsApp, typeVersion 1.1, credential whatsAppApi",
"source_url": "https://raw.githubusercontent.com/n8n-io/n8n/master/packages/nodes-base/nodes/WhatsApp/WhatsApp.node.ts",
"raw_snippet": "displayName: 'WhatsApp Business Cloud'",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "n8n-whatsapp-trigger-type",
"claim": "The n8n WhatsApp Trigger node type is n8n-nodes-base.whatsAppTrigger",
"source_url": "https://raw.githubusercontent.com/n8n-io/n8n/master/packages/nodes-base/nodes/WhatsApp/WhatsAppTrigger.node.ts",
"raw_snippet": "name: 'whatsAppTrigger'",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "meta-csw-24h",
"claim": "Meta opens a 24-hour customer service window when a WhatsApp user messages or calls the business",
"source_url": "https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-messages/",
"raw_snippet": "a 24-hour timer called a customer service window starts",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "browser",
"verified_at": "2026-09-14",
"verification_note": "Playwright 2026-09-14: the cited URL redirects to https://developers.facebook.com/documentation/business-messaging/whatsapp/messages/send-messages; location.href asserted; snippet read in the rendered page text."
},
{
"claim_id": "meta-template-categories",
"claim": "WhatsApp message templates fall into three categories: Marketing, Utility, Authentication",
"source_url": "https://developers.facebook.com/docs/whatsapp/pricing/",
"raw_snippet": "Marketing",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "browser",
"verified_at": "2026-09-14",
"verification_note": "Playwright 2026-09-14: the cited URL redirects to https://developers.facebook.com/documentation/business-messaging/whatsapp/pricing; location.href asserted; Marketing, Utility and Authentication all present in the rendered text."
},
{
"claim_id": "meta-per-message-pricing",
"claim": "WhatsApp conversation-based pricing was replaced by per-message pricing on July 1, 2025",
"source_url": "https://developers.facebook.com/docs/whatsapp/pricing/",
"raw_snippet": "Conversation-based pricing is deprecated. It was replaced with per-message pricing on July 1, 2025",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "browser",
"verified_at": "2026-09-14",
"verification_note": "Playwright 2026-09-14: the cited URL redirects to https://developers.facebook.com/documentation/business-messaging/whatsapp/pricing; location.href asserted; the exact snippet is present in the rendered text."
},
{
"claim_id": "n8n-30-docker-only",
"claim": "n8n 3.0 (scheduled October 2026) drops support for self-hosted npm/npx installs; Docker only",
"source_url": "https://docs.n8n.io/changelog/v30-breaking-changes.md",
"raw_snippet": "Self-hosted n8n will require a Docker-based deployment. n8n 3.0 will no longer support installations run using `npm` or `npx n8n`.",
"fetched_at": "2026-09-14T00:00:00+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md",
"references/n8n-version-migration.md"
]
},
{
"claim_id": "n8n-30-removed-nodes",
"claim": "n8n 3.0 removes the Function, Function Item and Item Lists nodes, the $getPairedItem helper, and AI Agent node v1",
"source_url": "https://docs.n8n.io/changelog/v30-breaking-changes.md",
"raw_snippet": "n8n 3.0 removes the deprecated `$getPairedItem` expression helper.",
"fetched_at": "2026-09-14T00:00:00+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md",
"references/n8n-version-migration.md"
]
},
{
"claim_id": "n8n-mcp-node-types",
"claim": "The n8n MCP nodes are @n8n/n8n-nodes-langchain.mcpClientTool and @n8n/n8n-nodes-langchain.mcpTrigger; no toolMcp node exists",
"source_url": "https://raw.githubusercontent.com/n8n-io/n8n/master/packages/%40n8n/nodes-langchain/nodes/mcp/McpClientTool/McpClientTool.node.ts",
"raw_snippet": "name: 'mcpClientTool'",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "ezcount-417-allocation",
"claim": "EZCount returns HTTP status 417 while a document waits for a Tax Authority allocation number, with four documented options",
"source_url": "https://documenter.getpostman.com/view/16363118/TzkyNLWB",
"raw_snippet": "When the document is waiting for the Tax Authority allocation number we will return status",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md",
"references/israeli-api-endpoints.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "curl 2026-09-14 with a desktop Chrome UA: HTTP 200, snippet present in raw bytes."
},
{
"claim_id": "ezcount-rate-limit",
"claim": "The EZCount API rate limit is 250 requests per 10 seconds and requests must be sequential",
"source_url": "https://documenter.getpostman.com/view/16363118/TzkyNLWB",
"raw_snippet": "The API has a rate limit of 250 requests per 10 seconds",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md",
"references/israeli-api-endpoints.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "curl 2026-09-14 with a desktop Chrome UA: HTTP 200, snippet present in raw bytes."
},
{
"claim_id": "ezcount-vat-type-default",
"claim": "EZCount vat_type defaults to PRE (price before VAT); INC means price includes VAT",
"source_url": "https://documenter.getpostman.com/view/16363118/TzkyNLWB",
"raw_snippet": "Price is before VAT",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "curl 2026-09-14 with a desktop Chrome UA: HTTP 200, snippet present in raw bytes."
},
{
"claim_id": "vat-s38a1-buyer-deduction",
"claim": "VAT Law s.38(a1) denies the buyer input-VAT deduction on a tax invoice exceeding 5,000 NIS before tax (10,000 from January to May 2026) without an allocation number, with no buyer-request condition",
"source_url": "https://he.wikisource.org/wiki/%D7%97%D7%95%D7%A7_%D7%9E%D7%A1_%D7%A2%D7%A8%D7%9A_%D7%9E%D7%95%D7%A1%D7%A3",
"raw_snippet": "לא יותר ניכוי מס התשומות הכלול בחשבונית מס שסכומה, בלא המס, עולה על 5,000 שקלים חדשים",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "vat-s47a2-seller-duty",
"claim": "VAT Law s.47(a2)(1) makes the seller duty to obtain an allocation number conditional on the buyer demand and excludes zero-rated transactions",
"source_url": "https://he.wikisource.org/wiki/%D7%97%D7%95%D7%A7_%D7%9E%D7%A1_%D7%A2%D7%A8%D7%9A_%D7%9E%D7%95%D7%A1%D7%A3",
"raw_snippet": "חייב הוא לעשות כן לפי דרישת הקונה; הוראות סעיף קטן זה יחולו לעניין חשבונית מס שהוצאה בשל עסקה שהמס שחל לגביה אינו בשיעור אפס",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "hebcal-israel-8-yomtov",
"claim": "Hebcal returns 8 yomtov days for Israel in 2026 with i=on, against 13 for the Diaspora schedule",
"source_url": "https://www.hebcal.com/hebcal?v=1&cfg=json&year=now&month=x&maj=on&mod=on&i=on",
"raw_snippet": "\"title\": \"Pesach I\", \"date\": \"2026-04-02\", \"hdate\": \"15 Nisan 5786\", \"category\": \"holiday\", \"subcat\": \"major\", \"yomtov\": true",
"fetched_at": "2026-08-27T18:52:02.730644+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "vat-section-38-he",
"claim": "Section 38 of the Israeli VAT Law governs input-VAT deduction",
"source_url": "https://he.wikisource.org/wiki/%D7%97%D7%95%D7%A7_%D7%9E%D7%A1_%D7%A2%D7%A8%D7%9A_%D7%9E%D7%95%D7%A1%D7%A3",
"raw_snippet": "ובעסקה שסכומה, בלא המס, עולה על הסכום האמור בסעיף 38(א1), חייב הוא לעשות כן לפי דרישת הקונה",
"fetched_at": "2026-08-27T18:52:32.812005+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "vat-section-47-he",
"claim": "Section 47 of the Israeli VAT Law governs the right to issue a tax invoice and the allocation-number request",
"source_url": "https://he.wikisource.org/wiki/%D7%97%D7%95%D7%A7_%D7%9E%D7%A1_%D7%A2%D7%A8%D7%9A_%D7%9E%D7%95%D7%A1%D7%A3",
"raw_snippet": "ושאינה כוללת מספר שהקצה לה המנהל לפי סעיף 47(א2)(2) או (א3)(1) או (א4)",
"fetched_at": "2026-08-27T18:52:32.812005+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "datagovil-ckan-help",
"claim": "The data.gov.il CKAN API exposes datastore_search_sql and documents it through the action help endpoint",
"source_url": "https://data.gov.il/api/3/action/help_show?name=datastore_search_sql",
"raw_snippet": "Execute SQL queries on the DataStore.",
"fetched_at": "2026-08-27T18:52:32.812005+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md",
"references/israeli-api-endpoints.md"
],
"verified_via": "probe",
"verified_at": "2026-09-14",
"verification_note": "GET re-run 2026-09-14: HTTP 200 JSON, snippet present in the result text."
},
{
"claim_id": "npm-dist-tags-endpoint",
"claim": "The npm registry dist-tags endpoint for the package, cited in the migration reference, serves its dist-tags as JSON, so the current stable pin can be re-checked there each cycle rather than trusted from a previous cycle",
"source_url": "https://registry.npmjs.org/-/package/n8n/dist-tags",
"raw_snippet": "{\"latest\":\"2.38.7\",\"stable\":\"2.38.7\"",
"fetched_at": "2026-09-14T00:00:00+00:00",
"appears_in": [
"references/n8n-version-migration.md"
],
"verification_note": "Raw endpoint bytes 2026-09-14 (HTTP 200). The registry serves compact JSON with no whitespace on every endpoint and Accept header, so a byte-exact quote is necessarily one token; this entry therefore asserts no figure. The version figure itself is backed by n8n-stable-2387 (GitHub releases API)."
},
{
"claim_id": "cardcom-returnvalue-passthrough",
"claim": "Cardcom v11 ReturnValue is a merchant-supplied pass-through echoed back, not a status field",
"source_url": "https://secure.cardcom.solutions/swagger/v11/swagger.json",
"raw_snippet": "A string of data to save on the transaction, usually send your unique order Id, you will get it back in the WebHook URL",
"fetched_at": "2026-08-27T19:21:54.446885+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md",
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "cardcom-responsecode-success",
"claim": "Cardcom v11 signals success with ResponseCode equal to zero",
"source_url": "https://secure.cardcom.solutions/swagger/v11/swagger.json",
"raw_snippet": "if equel zero then success , else , a develper error see Description for more info",
"fetched_at": "2026-08-27T19:21:54.446885+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md",
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "cardcom-lowprofileresult-fields",
"claim": "The Cardcom v11 LowProfileResult contains ResponseCode, Description, LowProfileId, TranzactionId, ReturnValue and TranzactionInfo, and contains no DealResponse field",
"source_url": "https://secure.cardcom.solutions/swagger/v11/swagger.json",
"raw_snippet": "\"LowProfileResult\": {\n \"type\": \"object\",\n \"description\": \"Result of the low profile page deal\",",
"fetched_at": "2026-08-27T19:21:54.446885+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md",
"references/israeli-api-endpoints.md"
]
},
{
"claim_id": "n8n-code-node-no-credentials",
"claim": "The n8n Code node's INodeTypeDescription declares no credentials array, so a Code node cannot read secrets from the credential store",
"source_url": "https://raw.githubusercontent.com/n8n-io/n8n/master/packages/nodes-base/nodes/Code/Code.node.ts",
"raw_snippet": "displayName: 'Code',\n\t\tname: 'code',\n\t\ticon: 'node:code',\n\t\ticonColor: 'amber',\n\t\tgroup: ['transform'],\n\t\tversion: [1, 2],\n\t\tdefaultVersion: 2,\n\t\tdescription: 'Run custom JavaScript or Python code',",
"fetched_at": "2026-08-27T19:21:54.446885+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md",
"references/webhook-auth-patterns.md"
]
},
{
"claim_id": "n8n-jwt-verify-enforces-exp",
"claim": "n8n Webhook JWT Auth calls jwt.verify from jsonwebtoken, which rejects expired tokens by default; only iss and aud go unchecked",
"source_url": "https://raw.githubusercontent.com/n8n-io/n8n/master/packages/nodes-base/nodes/Webhook/utils.ts",
"raw_snippet": "const payload = jwt.verify(token, secretOrPublicKey, { algorithms: [expectedAuth.algorithm] }) as IDataObject;",
"fetched_at": "2026-08-27T19:21:54.446885+00:00",
"appears_in": [
"references/webhook-auth-patterns.md"
]
},
{
"claim_id": "hebcal-chag-ordering-trap",
"claim": "On a chag, the Hebcal shabbat endpoint returns that festival havdalah before the next festival candle lighting, so a naive first-candles-to-first-havdalah range test can never match",
"source_url": "https://www.hebcal.com/shabbat?cfg=json&geonameid=293397&M=on&gy=2026&gm=9&gd=21",
"raw_snippet": "{\"title\":\"Havdalah: 19:15\",\"date\":\"2026-09-21T19:15:00+03:00\",\"category\":\"havdalah\",\"title_orig\":\"Havdalah\",\"hebrew\":\"הבדלה\",\"memo\":\"Yom Kippur\"},{\"title\":\"Erev Sukkot\",\"date\":\"2026-09-25\"",
"fetched_at": "2026-08-27T19:21:54.446885+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md",
"references/shabbat-cron-patterns.md"
]
},
{
"claim_id": "israel-spam-law-amendment-40",
"claim": "Section 30A(j)(1) of the Communications Law allows damages without proof of damage up to 1,000 NIS per advertisement received in breach",
"source_url": "https://he.wikisource.org/wiki/%D7%97%D7%95%D7%A7_%D7%94%D7%AA%D7%A7%D7%A9%D7%95%D7%A8%D7%AA_(%D7%91%D7%96%D7%A7_%D7%95%D7%A9%D7%99%D7%93%D7%95%D7%A8%D7%99%D7%9D)",
"raw_snippet": "רשאי בית המשפט לפסוק בשל הפרה זו פיצויים שאינם תלויים בנזק (בסעיף זה – פיצויים לדוגמה), בסכום שלא יעלה על 1,000 שקלים חדשים בשל כל דבר פרסומת שקיבל הנמען בניגוד להוראות סעיף זה",
"fetched_at": "2026-08-27T19:27:25.269532+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "israel-spam-law-consent",
"claim": "Section 30A(b) requires explicit prior written consent before sending an advertisement by SMS, email, fax or automated dialler",
"source_url": "https://he.wikisource.org/wiki/%D7%97%D7%95%D7%A7_%D7%94%D7%AA%D7%A7%D7%A9%D7%95%D7%A8%D7%AA_(%D7%91%D7%96%D7%A7_%D7%95%D7%A9%D7%99%D7%93%D7%95%D7%A8%D7%99%D7%9D)",
"raw_snippet": "לא ישגר מפרסם דבר פרסומת באמצעות פקסימיליה, מערכת חיוג אוטומטי, הודעה אלקטרונית או הודעת מסר קצר, בלא קבלת הסכמה מפורשת מראש של הנמען, בכתב",
"fetched_at": "2026-08-27T19:27:25.269532+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "israel-spam-law-sms-carveout",
"claim": "Section 30A(e)(2) is an SMS-specific carve-out: an advertisement sent by SMS states ONLY the advertiser name and the contact details for a refusal notice, so the general requirement to open with the word pirsomet does not apply to SMS",
"source_url": "https://he.wikisource.org/wiki/%D7%97%D7%95%D7%A7_%D7%94%D7%AA%D7%A7%D7%A9%D7%95%D7%A8%D7%AA_(%D7%91%D7%96%D7%A7_%D7%95%D7%A9%D7%99%D7%93%D7%95%D7%A8%D7%99%D7%9D)",
"raw_snippet": "על אף הוראות פסקה (1), מפרסם המשגר דבר פרסומת באמצעות הודעת מסר קצר יציין בדבר הפרסומת רק את שמו ואת דרכי יצירת הקשר עמו לצורך מתן הודעת סירוב",
"fetched_at": "2026-08-27T19:27:25.269532+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "telecom-section-30a-he",
"claim": "Section 30A of the Israeli Communications Law is the provision governing unsolicited commercial messages",
"source_url": "https://he.wikisource.org/wiki/%D7%97%D7%95%D7%A7_%D7%94%D7%AA%D7%A7%D7%A9%D7%95%D7%A8%D7%AA_(%D7%91%D7%96%D7%A7_%D7%95%D7%A9%D7%99%D7%93%D7%95%D7%A8%D7%99%D7%9D)",
"raw_snippet": "והגבלה של קבלת שיחות שבוצעו ממערכת חיוג אוטומטי כהגדרתה בסעיף 30א(א)",
"fetched_at": "2026-08-27T19:29:16.884365+00:00",
"appears_in": [
"SKILL.md",
"SKILL_HE.md"
]
},
{
"claim_id": "datagovil-sql-live-query",
"claim": "The CKAN action path /action/datastore_search_sql is live on data.gov.il: a query against the amutot resource returns HTTP 200 with \"success\": true and a records array, refuting the earlier claim that the action and SQL queries are blocked",
"source_url": "https://data.gov.il/api/3/action/datastore_search_sql?sql=SELECT%20*%20FROM%20%22be5b7935-3922-45d4-9638-08871b17ec95%22%20LIMIT%201",
"raw_snippet": "\"success\": true, \"result\": {\"sql\": \"SELECT * FROM \\\"be5b7935-3922-45d4-9638-08871b17ec95\\\" LIMIT 1\", \"records\": [",
"fetched_at": "2026-09-14T00:00:00+00:00",
"appears_in": [
"references/israeli-api-endpoints.md"
]
}
],
"generated_at": "2026-08-27T18:52:02.730644+00:00"
}
m.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--
This is a single-page Redoc site served behind CloudFront's SPA fallback
(custom_403/404 -> /index.html @ 200), so S3 misses return index.html with
an HTTP 200 for ANY path. Only "/" and "/api" are real routes (the docs);
every other path is treated as not-found. We flag it before first paint so
CSS can swap the docs for the #not-found view without a flash, and the app
bootstrap below skips Redoc entirely. Hash navigation (#tag/...) leaves
pathname unchanged, so deep links within an allowed route are unaffected.
NOTE: this is a client-side 404 — the HTTP status is still 200. A true
edge-level 404 would need a CloudFront Function on the distribution.
-->
<script>
(function () {
var path = window.location.pathname;
var known = path === '/' || path === '/api' || path === '/api/';
if (!known) {
window.__developersNotFound = true;
document.documentElement.className += ' route-not-found';
}
})();
</script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Morning API Documentation</title>
<link rel="icon" type="image/x-icon" href="https://static.greeninvoice.co.il/icons/favicon.ico">
<script type="module" crossorigin src="/assets/index-CdpFdn_R.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Dnu_In-I.css">
</head>
<body>
<!-- Morning-branded header -->
<div class="api-header">
<svg width="200" height="45" viewBox="0 0 200 45" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M72.661 35.6938V23.2206C72.661 20.9235 71.6731 19.3644 69.7926 19.3644C67.2933 19.3644 65.5318 21.7804 65.5318 25.5176V35.6938H59.9617V23.2206C59.9617 20.8045 58.9381 19.1977 57.01 19.1977C54.5463 19.1977 52.6658 21.6614 52.6658 25.601V35.6938H47.1315V15.1749H52.6658V18.8288C53.737 16.2818 56.1531 14.5679 59.1762 14.5679C62.1278 14.5679 64.3773 16.2937 65.1985 18.8764C66.3054 16.2461 68.6382 14.5679 71.7565 14.5679C76.0173 14.5679 78.2311 17.6862 78.2311 22.9825V35.7057H72.661V35.6938ZM80.9804 25.4343C80.9804 18.9121 85.2889 14.556 91.7516 14.556C98.2144 14.556 102.523 18.9002 102.523 25.4343C102.523 31.9566 98.2144 36.3127 91.7516 36.3127C85.2889 36.3127 80.9804 31.9566 80.9804 25.4343ZM86.7528 25.4343C86.7528 29.0882 88.7643 31.5043 91.7516 31.5043C94.739 31.5043 96.7504 29.0882 96.7504 25.4343C96.7504 21.7447 94.739 19.3644 91.7516 19.3644C88.7643 19.3644 86.7528 21.7447 86.7528 25.4343ZM116.793 22.0304C116.793 23.1373 116.389 24.3275 115.603 25.6367H121.375C122.078 24.2441 122.482 22.5541 122.482 20.9235C122.482 17.103 120.269 14.5679 116.829 14.5679C114.163 14.5679 112.151 16.0913 111.092 18.6741V15.1868H105.57V35.7057H111.104V25.6129C111.104 21.84 112.58 19.412 114.591 19.412C115.936 19.4001 116.793 20.4236 116.793 22.0304ZM130.647 25.601C130.647 21.7805 132.575 19.1977 135.241 19.1977C138.443 19.1977 138.443 22.1137 138.443 24.0418V35.6938H144.013V24.0061C144.013 21.1735 144.013 14.5679 137.372 14.5679C134.253 14.5679 131.837 16.1628 130.647 18.8288V15.1749H125.113V35.6938H130.647V25.601ZM147.81 35.6938H153.38V15.1749H147.81V35.6938ZM150.642 5.90333C148.762 5.90333 147.322 7.34346 147.322 9.22396C147.322 11.1045 148.762 12.5446 150.642 12.5446C152.523 12.5446 153.963 11.1045 153.963 9.22396C153.963 7.34346 152.523 5.90333 150.642 5.90333ZM169.78 14.556C166.662 14.556 164.246 16.1509 163.056 18.8169V15.163H157.521V35.6818H163.056V25.5891C163.056 21.7685 164.984 19.1858 167.65 19.1858C170.852 19.1858 170.852 22.1018 170.852 24.0299V35.6818H176.422V23.9942C176.422 21.1616 176.422 14.556 169.78 14.556ZM199.119 37.4552C199.119 41.6804 194.81 44.5131 189.288 44.5131C183.837 44.5131 179.611 41.728 178.754 37.2053H184.943C185.312 38.8001 186.907 39.7047 189.252 39.7047C191.549 39.7047 193.31 38.8001 193.31 37.1577C193.31 35.5628 191.751 34.4084 188.478 34.4084C186.681 34.4084 184.467 34.7416 182.575 35.2296V31.5757C186.015 31.171 188.597 30.2189 190.406 28.9454C189.585 29.1477 188.681 29.2786 187.705 29.2786C182.706 29.2786 179.302 26.5293 179.302 22.2208C179.302 17.6624 183.11 14.5441 188.847 14.5441C191.799 14.5441 194.215 15.3653 195.857 16.8055C196.226 15.5796 196.06 14.0204 195.738 12.616H199.88C200.333 14.9964 199.476 17.7457 197.786 19.3882C198.154 20.2451 198.357 21.1973 198.357 22.2208C198.357 25.5891 196.191 28.8621 192.251 31.0877C196.417 31.7066 199.119 34.0513 199.119 37.4552ZM192.561 22.316C192.561 20.5546 191.085 19.3644 188.835 19.3644C186.586 19.3644 185.062 20.5546 185.062 22.316C185.062 24.0775 186.574 25.2677 188.835 25.2677C191.085 25.2677 192.561 24.0775 192.561 22.316ZM8.25991 1.45203C7.39107 0.559389 6.16518 0 4.82026 0C2.15424 0 0 2.15424 0 4.82026V12.1399C0 14.8059 2.15424 16.9602 4.82026 16.9602H12.1399C14.8059 16.9602 16.9602 14.8059 16.9602 12.1399C16.9602 10.795 16.4008 9.56911 15.5082 8.70028L8.25991 1.45203ZM30.8854 0C29.5405 0 28.3146 0.559389 27.4457 1.45203L20.1975 8.70028C19.3049 9.58102 18.7455 10.795 18.7455 12.1399C18.7455 14.8059 20.8997 16.9602 23.5657 16.9602H30.8854C33.5514 16.9602 35.7057 14.8059 35.7057 12.1399V4.82026C35.7057 2.15424 33.5514 0 30.8854 0ZM12.1399 18.7455H4.82026C2.15424 18.7455 0 20.8997 0 23.5657V30.8854C0 33.5514 2.15424 35.7057 4.82026 35.7057H12.1399C14.8059 35.7057 16.9602 33.5514 16.9602 30.8854V23.5657C16.9602 20.8997 14.8059 18.7455 12.1399 18.7455ZM30.8854 18.7455H23.5657C20.8997 18.7455 18.7455 20.8997 18.7455 23.5657V30.8854C18.7455 33.5514 20.8997 35.7057 23.5657 35.7057H30.8854C33.5514 35.7057 35.7057 33.5514 35.7057 30.8854V23.5657C35.7057 20.8997 33.5514 18.7455 30.8854 18.7455Z" fill="#ffffff"/>
</svg>
<div class="divider"></div>
<span class="header-title">API Documentation</span>
</div>
<!-- Redocly container -->
<div id="redoc-container"></div>
<!-- Not-found view — shown by CSS when <html> carries .route-not-found -->
<div id="not-found">
<div class="not-found__code">404</div>
<h1 class="not-found__title">Page not found</h1>
<p class="not-found__text">
The page you’re looking for doesn’t exist. It may have been moved, or the address may be mistyped.
</p>
<a class="not-found__link" href="/">Back to the documentation</a>
</div>
<!-- Redocly standalone bundle (vendored from the redoc npm package by vite-plugin-static-copy) -->
<script src="/redoc.standalone.js"></script>
<!-- Application script — skipped on the not-found route (see the head guard) -->
</body>
</html>
metadata.json
{
"author": "skills-il",
"version": "2.5.1",
"category": "developer-tools",
"tags": {
"he": [
"n8n",
"אוטומציה",
"תהליכי-עבודה",
"ישראל",
"Morning",
"EZCount",
"תשלומים",
"שבת",
"API",
"AI-Agent",
"MCP",
"RAG",
"ביט",
"וואטסאפ"
],
"en": [
"n8n",
"automation",
"workflows",
"israel",
"morning",
"ezcount",
"payments",
"shabbat",
"api",
"ai-agent",
"mcp",
"rag",
"bit",
"whatsapp"
]
},
"display_name": {
"he": "תהליכי עבודה n8n בעברית",
"en": "n8n Hebrew Workflows"
},
"display_description": {
"he": "בונים וממטבים תהליכי n8n 2.x (היציב 2.38 נכון לספטמבר 2026) עם חיבורים ל-API ישראליים: Morning (חשבונית ירוקה), EZCount, israeli-bank-scrapers, data.gov.il, שערי SMS ושערי תשלום (Cardcom v11, Tranzila API v2, Grow by Meshulam). מכסה את קו הטלאים הקריטי של CVE-2026-44789, צמתי AI Agent עם LangChain מובנה ו-RAG (Pinecone/Qdrant/Supabase pgvector), צמתי MCP Client Tool ו-MCP Server Trigger, רפורמת חשבוניות 2026 (סף 5,000 ש\"ח לפני מע\"מ מיוני), תשלומי ביט, טיפול בעברית, חסימת שבת וחגים לפי לוח ישראל, ואירוח עצמי בענן ישראלי. אל תשתמשו בסקיל לעריכת n8n כללית בלי הקשר ישראלי.",
"en": "Build and optimize n8n 2.x automation workflows (stable line 2.38 as of September 2026) with Israeli API integrations including Morning (formerly Green Invoice), EZCount, israeli-bank-scrapers, data.gov.il, SMS gateways, and payment processors (Cardcom v11, Tranzila API v2, Grow by Meshulam). Covers the CVE-2026-44789 critical patch line, AI Agent nodes with native LangChain and RAG (Pinecone/Qdrant/Supabase pgvector), MCP Client Tool and MCP Server Trigger nodes, Israel Invoice Reform 2026 (5,000 NIS pre-VAT threshold from June), Bit payments, Hebrew data handling, Israel-schedule Shabbat and holiday gating, and self-hosting on Israeli cloud. Do NOT use for general n8n tutorials without Israeli context."
},
"supported_agents": [
"claude-code",
"cursor",
"github-copilot",
"windsurf",
"opencode",
"codex",
"gemini-cli"
]
}optimization-log.json
{
"cycles": [
{
"version": "2.5.0",
"date": "2026-08-27",
"holdout_before": null,
"holdout_after": null,
"lessons": [
{
"id": "carry-resolved-whatsapp",
"status": "resolved",
"summary": "The deferred WhatsApp coverage from 2.4.0 landed. Node types verified against n8n master source (n8n-nodes-base.whatsApp typeVersion 1.1, n8n-nodes-base.whatsAppTrigger), plus the 24-hour customer service window, the three template categories, and per-message pricing (conversation pricing ended 2025-07-01)."
},
{
"id": "carry-resolved-n8n-3",
"status": "resolved",
"summary": "The n8n 3.0 readiness subsection landed, sourced from docs.n8n.io/changelog/v30-breaking-changes.md. Beyond the four items the prior cycle listed, 3.0 also removes AI Agent node v1 and every legacy agent mode, which hits Step 7 directly."
},
{
"id": "carry-resolved-model-names",
"status": "resolved",
"summary": "The hardcoded model names (Claude 3.5 Sonnet, GPT-4o, Opus 4.7) are gone. The prior cycle could not verify the node's model list because it looked at the wrong path; the directory is LMChatAnthropic (capital M) and the current node resolves its model list from the Anthropic API at runtime rather than a hardcoded dropdown. Replaced with a selection principle so this cannot go stale again."
},
{
"id": "deferred-morning-allocation-mechanism",
"status": "deferred",
"summary": "The skill states the allocation thresholds, the before-VAT rule and the `עולה על` boundary correctly, and now carries both governing statute limbs verbatim, but it still does not say HOW a Morning-issued document obtains an allocation number, nor what to do when the request is refused. EZCount's path is documented (417 plus four options); Morning's is not. An agent will invent a field name here.",
"action": "Next cycle: get the Morning allocation flow from a source that is not a JS-only SPA, or state explicitly that it is undocumented publicly and route to Morning support. Do not guess a field name.",
"why_deferred": "developers.morning.co/api is a Redoc SPA behind a CloudFront fallback that serves the shell with HTTP 200 for every path, so no field name could be confirmed this cycle."
},
{
"id": "deferred-hebrew-word-cap",
"status": "deferred",
"summary": "SKILL_HE.md is 6,079 words against the 5,000 cap. This cycle cut it from 6,967 by moving five sections to references, but it is still over. The English file is at 5,000 exactly.",
"action": "Next cycle: move the Morning and EZCount request-body blocks out of SKILL_HE.md into references/israeli-api-endpoints.md, which should clear the remaining ~1,100 words.",
"why_deferred": "The cap is a warning rather than an error for the Hebrew file, and this cycle's budget went to twelve CRITICAL correctness findings."
},
{
"id": "deferred-queue-and-drain-pattern",
"status": "deferred",
"summary": "The skill now warns that `return []` DROPS work rather than deferring it, and says to queue and drain after havdalah, but ships no worked queue-and-drain pattern. shabbat-cron-patterns Pattern 5 gestures at draining a queue that nothing in the skill fills.",
"action": "Next cycle: write the queue-and-drain pair as a concrete pattern (static data or DB write on block, post-havdalah drain workflow).",
"why_deferred": "Deferred over the Phase 4.1 discretionary cap; the correctness half (do not silently drop) shipped."
}
],
"persistent_fail": [
{
"id": "stale-version-pins",
"summary": "Third consecutive cycle where the pinned version was stale on arrival. v2.3.0 pinned 2.21.4; v2.4.0 pinned 2.32.7 and called it current stable; at this review npm stable is 2.36.7 with beta 2.37.3. The 2.32.1 security FLOOR held (no advisory has a first_patched_version above it), so only the 'current stable' claim was wrong, but it was wrong in both languages and in the Docker Compose image tag.",
"recurrence_guard": "Every review must re-query https://registry.npmjs.org/-/package/n8n/dist-tags AND https://api.github.com/advisories?ecosystem=npm&affects=n8n, compute the highest first_patched_version to get the floor, and treat the floor and the current stable as two SEPARATE claims. Then grep BOTH language files and the Compose block for the old number: in this cycle the Hebrew half of a reference file kept 2.32.7 after the English half was corrected."
},
{
"id": "silent-failure-endpoints",
"summary": "Held again, and it caught two fabrications this cycle. The MCP Client Tool type `@n8n/n8n-nodes-langchain.toolMcp` does not exist (correct: `mcpClientTool` / `mcpTrigger`), and the EZCount `allocation_status: 'pending'` field does not exist (EZCount returns HTTP 417 with four documented options, and retrying never clears it). Both were plausible-looking and neither would have been caught by reading.",
"recurrence_guard": "Every node type string, env var name and API field name in this skill must be checked against upstream SOURCE (the n8n GitHub repo, the vendor's OpenAPI or Postman collection), never recalled. Note that a GET probe is not a test of a POST endpoint: 019sms.co.il/api returns 404 on GET and the documented 200-with-status-3 body on POST, and a reviewer wrongly concluded the endpoint was a marketing page."
},
{
"id": "fabricated-field-semantics",
"summary": "New this cycle, and the most serious defect found. The skill documented Cardcom's `ReturnValue` as the success flag ('0 = success'). Cardcom's own v11 OpenAPI defines it as a merchant-supplied pass-through echoed back from CreateLowProfile; the success field is `ResponseCode`. The rest of the callback table was legacy classic-API naming: `DealResponse` has zero occurrences in the v11 spec, so the recommended `InternalDealNumber` dedup key was always undefined and the duplicate-invoice guard never fired. The wrong table was live in SKILL.md, SKILL_HE.md and twice in references/.",
"recurrence_guard": "A field name being real is not the same as its SEMANTICS being right, and a field that exists in an older API version is the most dangerous case because it looks verifiable. For every field the skill branches on, read the vendor's machine-readable schema and quote the field's own description, not just its presence. Then grep every file, both languages, for the old field names."
},
{
"id": "gate-logic-never-executed",
"summary": "The skill's headline feature, the Shabbat gate, was broken for the exact case it exists to prevent, in every copy of it (SKILL.md, SKILL_HE.md, and two patterns in references/). Taking items.find('candles') and items.find('havdalah') and testing a range fails ON a chag, because Hebcal returns that chag's havdalah BEFORE the next festival's candle lighting, making the test unsatisfiable. Reproduced against the live API for Yom Kippur and Pesach I. The gate also failed OPEN on any Hebcal degradation, and its yomtov branch used a UTC date-equality test that cannot express erev chag.",
"recurrence_guard": "Prose correctness is not code correctness: every factual statement ABOUT the Hebcal parameters in this skill was right while the code consuming them was wrong. Any control-flow gate in this skill must be traced against real API responses for the adversarial date (on the chag, on erev chag, at 01:00 Israel time), not just the ordinary one, and must be checked for whether its failure mode is open or closed."
}
]
}
]
}
references/.gitkeep
references/israeli-api-endpoints.md
# Israeli API Endpoints Reference for n8n
Quick reference for configuring HTTP Request nodes when connecting to Israeli services.
## Morning (formerly Green Invoice) API
Base URL: `https://api.greeninvoice.co.il/api/v1`
Note: The company rebranded from "Green Invoice" to "Morning" (חשבונית ירוקה). The API domain remains `api.greeninvoice.co.il`.
### Authentication
| Step | Method | Endpoint | Body |
|------|--------|----------|------|
| Get token | POST | `/account/token` | `{ "id": "<api_key>", "secret": "<api_secret>" }` |
Authentication is API key + secret -> JWT. This is NOT OAuth2.
Token TTL: 60 minutes. Refresh proactively before expiry.
### Document Endpoints
| Endpoint | Method | Description | Key Parameters |
|----------|--------|-------------|----------------|
| `/documents/search` | POST | Search invoices/receipts | `fromDate`, `toDate`, `type`, `status`, `client` |
| `/documents` | POST | Create document | `type`, `client`, `income` (line items array) |
| `/documents/{id}` | GET | Get document by ID | Path parameter: document UUID |
| `/documents/{id}/download/links` | GET | Get download links for the document | Returns URLs, not the binary itself |
There is no endpoint that emails an existing document. Delivery is a property
of creation: pass `emailContent` and `attachment` on `POST /documents`.
### Document Types (type field)
| Code | Type (Hebrew) | Type (English) |
|------|--------------|----------------|
| 10 | הצעת מחיר | Price Quote |
| 305 | חשבונית מס | Tax Invoice |
| 320 | חשבונית מס / קבלה | Tax Invoice / Receipt |
| 330 | חשבונית זיכוי | Credit Note / Refund |
| 400 | קבלה | Receipt |
### Israel Invoice Reform 2026
Tax invoices (type 305, 320) over the threshold require an allocation number (mispar haktza'a) from the Israel Tax Authority via SHAAM clearance. Threshold schedule:
| Effective | Threshold |
|-----------|-----------|
| May 2024 | 25,000 NIS |
| Jan 2025 | 20,000 NIS |
| Jan 1, 2026 | 10,000 NIS |
| **Jun 1, 2026 onwards** | **5,000 NIS** |
**Thresholds are before VAT** (lifnei maam). Compare against the `amount` field, NOT `totalAmount`. No further step-down is legislated beyond June 2026.
When creating documents via API, check Morning's documentation for the allocation workflow applicable to API-created documents. Build the threshold as a workflow variable rather than a hardcoded literal.
### Client Endpoints
| Endpoint | Method | Description | Key Parameters |
|----------|--------|-------------|----------------|
| `/clients/search` | POST | Search clients | `name`, `taxId`, `email` |
| `/clients` | POST | Create client | `name`, `taxId`, `emails`, `address` |
| `/clients/{id}` | PUT | Update client | Full client object |
### Payment Endpoints
| Endpoint | Method | Description | Key Parameters |
|----------|--------|-------------|----------------|
| `/documents/payments/search` | POST | Search payments recorded on documents | `fromDate`, `toDate` |
| `/payments/form` | POST | Create a hosted payment form | |
| `/payments/tokens/search` | POST | Search stored payment tokens | |
| `/payments/tokens/{id}/charge` | POST | Charge a stored token | Path parameter: token ID |
Note there is no `GET /payments` collection; reconciliation goes through
`POST /documents/payments/search`.
### Common Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Document UUID |
| `number` | integer | Document number (sequential) |
| `amount` | number | Amount before VAT (in decimal shekels, NOT agorot) |
| `vat` | number | VAT amount (in decimal shekels) |
| `totalAmount` | number | Amount including VAT (in decimal shekels) |
| `status` | integer | 0=draft, 10=open, 20=closed, 30=canceled |
| `createdAt` | string | ISO 8601 timestamp |
| `client.name` | string | Client name (may be Hebrew) |
| `client.taxId` | string | Israeli tax ID (osek morshe/patur number) |
**Amounts are in decimal shekels.** `amount: 50` means 50.00 NIS. Do not multiply or divide by 100.
---
## EZCount (EasyCount) API
Base URL: `https://api.ezcount.co.il/api`
EZCount is a Morning alternative for SMB invoicing in Israel.
### Authentication
Authentication via `api_key` + `api_email` in the request body (not OAuth, not Bearer).
### Document Endpoints
| Endpoint | Method | Description | Key Parameters |
|----------|--------|-------------|----------------|
| `/createDoc` | POST | Create document | `type`, `customer_name`, `item[]`, `api_key`, `api_email` |
| `/getLastAPIRequestData` | GET | Inspect the last API request (debugging) | `debug_key`, `api_key` |
| `/documents/getTaxMinistryAllocationNumber/{DOC_UUID}` | POST | Fetch the SHAAM allocation number | Path parameter: document UUID |
Only the calls above appear in EZCount's published Postman collection
(linked from ezcount.co.il/api). Endpoint names for searching documents,
downloading a PDF, or emailing a document are not documented publicly, and
`/searchDocuments` returns a 404 page. Read the exact names off the API tab
in your own EZCount account (Settings > API) rather than guessing them.
Note when probing this host: unknown paths return HTTP 200 with a
`MULTIPE POST PROBLEM` body rather than a 404, so a 200 here is not evidence
that an endpoint exists.
### Document Type Codes
Same Tax Authority codes as Morning: 10 (price quote), 305 (tax invoice), 320 (tax invoice / receipt), 330 (credit note), 400 (receipt).
### Israel Invoice Reform 2026
EZCount handles the SHAAM allocation request as part of document creation. When the Tax Authority has not (yet) allocated a number, the API returns **HTTP status `417`**, verbatim from the docs: "When the document is waiting for the Tax Authority allocation number we will return status `417`". There is no `allocation_status` field and no retry that clears a 417. The docs give four options at that point: skip the allocation number, cancel the document, file a further objection, or reverse charge (cancel and re-issue at zero-rate VAT, with the buyer issuing a self-invoice). You may also set your own allocation number. Where the status is 417 with error code 460, the Tax Authority's Hebrew refusal text must be surfaced to the document creator. Rate limit: 250 requests per 10 seconds, sequential rather than parallel.
### Common Response Fields
| Field | Type | Description |
|-------|------|-------------|
| `success` | boolean | Operation result |
| `errMsg` | string | Error in Hebrew |
| `docNum` | string | Document number |
| `pdfLink` | string | Public PDF URL |
| `allocation_number` | string | SHAAM-issued mispar haktza'a (Invoice Reform 2026) |
**Amounts are in decimal shekels.** Same convention as Morning.
---
## data.gov.il CKAN API
Base URL: `https://data.gov.il/api/3`
### Core Endpoints
| Endpoint | Method | Description | Key Parameters |
|----------|--------|-------------|----------------|
| `/action/datastore_search` | GET | Search within a dataset | `resource_id`, `q`, `filters`, `limit`, `offset`, `sort` |
| `/action/package_show` | GET | Get dataset metadata | `id` (dataset name or UUID) |
| `/action/resource_show` | GET | Get resource details | `id` (resource UUID) |
| `/action/datastore_search_sql` | GET | Run SQL against a resource | `sql` |
**`datastore_search_sql` DOES work on data.gov.il.** An earlier version of this
reference stated it was unavailable and that a WAF blocked SQL in the query
string. Both are false: `help_show?name=datastore_search_sql` returns the real
CKAN docstring ("Execute SQL queries on the DataStore"), and a live query
against the amutot resource returns records with HTTP 200. Quote the resource id
and URL-encode the statement:
```
GET https://data.gov.il/api/3/action/datastore_search_sql?sql=SELECT%20*%20FROM%20%22be5b7935-3922-45d4-9638-08871b17ec95%22%20LIMIT%201
```
Note the opposite quirk on this instance: `help_show?name=datastore_search`
returns a broken Python `partial(func, *args, **keywords)` docstring rather than
the action's own documentation, so use the `datastore_search_sql` help entry or
upstream CKAN docs when you need the parameter reference.
### Useful Resource IDs
| Dataset | Resource ID | Content | Update Frequency |
|---------|-------------|---------|-----------------|
| Non-Profit Registry (amutot) | be5b7935-3922-45d4-9638-08871b17ec95 | Registered non-profits | Weekly |
| Licensed Businesses | varies by municipality | Licensed businesses per city | Monthly |
| Election Results | varies by election | Voting results by ballot box | After elections |
Note: The Companies Registry resource ID may change. Verify the current resource ID via the data.gov.il portal before using.
### Query Examples
Search non-profits by name:
```
GET https://data.gov.il/api/3/action/datastore_search?resource_id=be5b7935-3922-45d4-9638-08871b17ec95&q=עמותה
```
### Response Format
```json
{
"success": true,
"result": {
"records": [...],
"total": 12345,
"fields": [
{ "id": "field_name", "type": "text" }
]
}
}
```
Note: Field names are in Hebrew. Normalize to English keys in a Code node for downstream compatibility.
---
## Israeli SMS Gateways
### 019 Telzar
Base URL: `https://019sms.co.il/api`
| Endpoint | Method | Description | Auth |
|----------|--------|-------------|------|
| `/api` | POST | All operations, including bulk sends | Bearer token in header |
019 exposes a single `/api` endpoint; the operation is selected in the request
body, not by the path. There are no `/api/bulk` or `/api/status` routes. A POST
to either returns the same generic HTTP 500 page as a path that was never
defined, and a GET returns a byte-identical 4,242-byte catch-all HTML page for
`/api/bulk`, `/api/status` and `/api/zzz_nonexistent` alike. Only `/api` itself
answers with JSON (an auth error). On this host, judge a route by whether the
body is JSON, not by the status code.
Send SMS request:
```
Headers:
Content-Type: application/json
Authorization: Bearer <token>
Body:
{
"from": "MyBusiness",
"to": "972501234567",
"message": "הודעה בעברית"
}
```
### InforUMobile
Base URL: `https://capi.inforu.co.il`
Note: `api.inforu.co.il` returns 404 for the v2 paths. The API host is `capi.inforu.co.il`.
InforUMobile has a legacy XML API and newer JSON API:
JSON API endpoint: `https://capi.inforu.co.il/api/v2/SMS/SendSms`
| Endpoint | Method | Description | Auth |
|----------|--------|-------------|------|
| `/api/v2/SMS/SendSms` | POST | Send SMS | Bearer token in header + IP allowlist |
`SendSms` is the only v2 path confirmed to exist (it answers 401 unauthenticated).
`GetSmsStatus` returns 404, identical to an undefined path, so it is not the
delivery-status endpoint. Take the correct path from InforU's own documentation
for your account rather than guessing a name.
Auth failures return HTTP 401 with `{"StatusId": -2, "StatusDescription": "Authentication failed or illegal IP address"}`. The same status covers a bad token and an unlisted source IP, so whitelist your n8n egress IP in the InforU dashboard before debugging the token.
Send SMS body (JSON API):
```json
{
"Message": "הודעה בעברית",
"Recipients": [{ "Phone": "972501234567" }],
"Settings": {
"Sender": "MyBusiness",
"MessageType": 1
}
}
```
### Phone Number Format Rules
| Input Format | Converted Format | Notes |
|-------------|-----------------|-------|
| 050-1234567 | 972501234567 | Strip dash and leading 0, add 972 |
| 0501234567 | 972501234567 | Strip leading 0, add 972 |
| +972501234567 | 972501234567 | Strip + prefix |
| 972501234567 | 972501234567 | Already correct |
| 05012345678 | Invalid | Israeli mobile is 10 digits total |
Israeli mobile prefixes: 050, 051, 052, 053, 054, 055, 058
---
## Israeli Payment Gateways
### Cardcom
Documentation: `https://www.cardcom.solutions/`
| Endpoint | Method | Description |
|----------|--------|-------------|
| `https://secure.cardcom.solutions/api/v11/LowProfile/Create` | POST | v11 hosted payment page |
| `https://secure.cardcom.solutions/api/v11/Transactions/Transaction` | POST | v11 direct charge |
| `https://secure.cardcom.solutions/Interface/ChargeToken.aspx` | POST | Charge a stored token (legacy) |
| `https://secure.cardcom.solutions/Interface/CreateInvoice.aspx` | POST | Create invoice after charge (legacy) |
| Callback URL (configured via API v11 or merchant dashboard) | POST | Payment result notification |
`https://secure.cardcom.solutions/api/v11` is a base path and returns 404 on its own. Always append the operation.
Callback fields, from the v11 `LowProfileResult` schema (`https://secure.cardcom.solutions/swagger/v11/swagger.json`):
| Field | Description |
|-------|-------------|
| `ResponseCode` | **`0` = success**, anything else is a failure; read `Description` |
| `Description` | Response description |
| `LowProfileId` | Hosted-payment-page id |
| `TranzactionId` | Cardcom transaction id, and the correct dedup key |
| `ReturnValue` | Your OWN pass-through value, echoed back. **Not a status.** |
| `TranzactionInfo` | Nested: `CardOwnerIdentityNumber` (teudat zehut), `NumberOfPayments`, `ApprovalNumber`, amount |
| `DocumentInfo` | Document details where Cardcom also issued the invoice |
| `TokenInfo` | Card token for future charges |
`ReturnValue` is documented by Cardcom on the request as "A string of data to save on the transaction, usually send your unique order Id, you will get it back in the WebHook URL" and on the result as "Same value that was sent on the CreateLowProfile request". `DealResponse` does not exist in v11 (zero occurrences in the spec), and `InternalDealNumber` / `CardOwnerID` / `NumOfPayments` are legacy classic-API names absent from the result object.
### Tranzila
Documentation: `https://docs.tranzila.com/`
Tranzila API v2 authenticates via the `X-tranzila-api-app-key` HTTP header (not Basic Auth, not query parameters). v2 covers server-to-server (SAQ-D), iframe, hosted fields, Bit (init returns an iframe URL with QR + push), tokenization, recurring billing, refunds, and 3D Secure.
| Endpoint | Method | Description |
|----------|--------|-------------|
| `https://secure5.tranzila.com/cgi-bin/tranzila31.cgi` | GET/POST | Process payment (legacy CGI, avoid for new integrations) |
| `https://api.tranzila.com/v1/transaction/credit_card/create` | POST | v2 server-to-server charge (auth: `X-tranzila-api-app-key`) |
| `https://api.tranzila.com/v1/transaction/bit/init` | POST | v2 Bit init, response contains iframe URL with QR code |
| Callback URL (configured in terminal settings) | GET | Payment result via query params |
Note the `credit_card` and `bit` segments in the v2 paths. `/v1/transaction/create` and `/v1/bit/init` do not exist and return 404. Unauthenticated calls to the correct paths return `401 {"code":401,"message":"Unauthorized"}`, which is the quickest way to confirm a path before wiring credentials. The legacy `tranzila71dl.cgi` endpoint also 404s; `tranzila31.cgi` is the surviving CGI entry point.
Callback query parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
| Response | string | "000" = approved |
| index | string | Transaction index |
| sum | string | Amount (decimal) |
| currency | string | "1"=ILS, "2"=USD, "3"=GBP, "7"=EUR |
| Rone | string | Installment count |
| ConfirmationCode | string | Shva confirmation code |
| ccno | string | Masked card number |
Tranzila API v2 also supports Bit payments. For new integrations, prefer v2 over the legacy CGI pattern.
### Grow by Meshulam
Documentation: `https://grow-il.readme.io/`
| Endpoint | Method | Description |
|----------|--------|-------------|
Base URL: `https://secure.meshulam.co.il` (sandbox: `https://sandbox.meshulam.co.il`)
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/light/server/1.0/createPaymentProcess/` | POST | Create payment page |
| `/api/light/server/1.0/getPaymentProcessInfo/` | POST | Get payment status |
| `/api/light/server/1.0/approveTransaction/` | POST | Approve transaction (required after webhook) |
| Webhook URL (configured in dashboard) | POST | Payment result |
Every operation is POST, including the status read. The API replies to an
undefined path with `{"err":"unknown method"}` and HTTP 200, so check the
`err` field rather than the status code when confirming a path.
**Important:** Grow API requests use `multipart/form-data`, not JSON.
Webhook payload fields:
| Field | Type | Description |
|-------|------|-------------|
| webhookKey | string | Webhook verification key |
| transactionCode | string | Unique transaction code |
| transactionType | string | Type of transaction |
| asmachta | string | Transaction reference number |
| paymentSum | string | Amount charged |
| paymentDate | string | Date of payment |
| fullName | string | Customer name (may be Hebrew) |
| payerPhone | string | Customer phone |
| payerEmail | string | Customer email |
| cardSuffix | string | Last 4 digits of card |
| cardBrand | string | Card brand (Visa, Mastercard, etc.) |
| paymentsNum | string | Installment count |
**After receiving a webhook, you must call `approveTransaction` to finalize the payment.**
Grow also supports Bit payments when enabled in the merchant dashboard.
---
## Hebcal API
Base URL: `https://www.hebcal.com`
### Shabbat Times
| Endpoint | Method | Description | Key Parameters |
|----------|--------|-------------|----------------|
| `/shabbat` | GET | Shabbat candle lighting and havdalah | `cfg=json`, `geonameid`, `M=on` |
### Holiday Calendar
| Endpoint | Method | Description | Key Parameters |
|----------|--------|-------------|----------------|
| `/hebcal` | GET | Jewish holidays | `v=1`, `cfg=json`, `year`, `month`, `maj=on`, `mod=on` |
### Israeli City Geoname IDs
| City | Geoname ID | Candle Lighting |
|------|-----------|-----------------|
| Jerusalem (yerushalayim) | 281184 | 40 min before sunset |
| Tel Aviv (tel aviv-yafo) | 293397 | 18 min before sunset |
| Haifa (haifa) | 294801 | 30 min before sunset |
| Zikhron Ya'akov | 293067 | 30 min before sunset |
| Beer Sheva (be'er sheva) | 295530 | 18 min before sunset |
| Rishon LeZion | 293703 | 18 min before sunset |
| Petah Tikva | 293918 | 18 min before sunset |
| Ashdod | 295629 | 18 min before sunset |
| Netanya | 294098 | 18 min before sunset |
| Bnei Brak | 295514 | 18 min before sunset |
| Holon | 294751 | 18 min before sunset |
| Ramat Gan | 293768 | 18 min before sunset |
| Herzliya | 294778 | 18 min before sunset |
### Shabbat Response Format
```json
{
"title": "Shabbat Times for Tel Aviv-Yafo",
"date": "2026-01-16",
"items": [
{
"title": "Candle lighting: 4:38pm",
"date": "2026-01-16T16:38:00+02:00",
"category": "candles",
"memo": "Parashat Beshalach"
},
{
"title": "Havdalah (50 min): 5:42pm",
"date": "2026-01-17T17:42:00+02:00",
"category": "havdalah"
}
]
}
```
### Holiday Response Fields
| Field | Type | Description |
|-------|------|-------------|
| title | string | Holiday name in English |
| hebrew | string | Holiday name in Hebrew |
| date | string | ISO 8601 date |
| category | string | "holiday", "candles", "havdalah" |
| yomtov | boolean | True if work restrictions apply |
| memo | string | Additional info (Torah portion, etc.) |
## israeli-bank-scrapers login fields (per bank)
**Login fields vary per bank.** There is no universal credential shape. Read `SCRAPERS[companyId].loginFields` before wiring credentials:
| Bank | Required fields |
|------|----------------|
| hapoalim | `userCode`, `password` |
| leumi, mizrahi, otsarHahayal, max, visaCal, union, beinleumi, massad, yahav | `username`, `password` |
| discount, mercantile | `id`, `password`, `num` |
| isracard, amex | `id`, `card6Digits`, `password` |
## Gateway field reference (relocated from SKILL.md)
| Business Need | n8n Pattern | Key Nodes | Israeli API |
|--------------|-------------|-----------|-------------|
| Invoice reconciliation | Schedule -> HTTP -> Compare -> Update | Schedule, HTTP, IF, Code | Morning (Green Invoice) |
| Bank transaction categorization | Schedule -> Code -> Spreadsheet | Schedule, Code, Sheets | israeli-bank-scrapers |
| Government data sync | Schedule -> HTTP -> Transform -> DB | Schedule, HTTP, Code, Postgres | data.gov.il CKAN |
| SMS notifications | Trigger -> Code -> HTTP | Webhook, Code, HTTP | 019 Telzar / InforUMobile |
| Payment webhook handling | Webhook -> Validate -> Process | Webhook, IF, Code, HTTP | Cardcom / Tranzila / Grow |
| Holiday-aware scheduling | Schedule -> HTTP -> IF -> Execute | Schedule, HTTP, IF, Code | Hebcal |
| AI-powered categorization | Schedule -> Code -> AI Agent -> DB | Schedule, Code, AI Agent, Postgres | israeli-bank-scrapers + LLM |
| Invoice Reform compliance | Webhook -> Code -> HTTP -> HTTP | Webhook, Code, HTTP | Morning + Tax Authority allocation |
---
# Israeli Payment Gateway Callbacks (moved from SKILL.md Step 5)
## Cardcom
**`ReturnValue` is NOT the success flag.** Cardcom's v11 spec defines it on the request as "A string of data to save on the transaction, usually send your unique order Id, you will get it back in the WebHook URL", and on the result as "Same value that was sent on the CreateLowProfile request". It is your own pass-through. Branching on `ReturnValue == 0` approves declined payments whenever the merchant happened to send something else. The success field is **`ResponseCode`** ("if equel zero then success").
The v11 `LowProfileResult` delivered to your `WebHookUrl` carries:
| Field | Description |
|-------|-------------|
| `ResponseCode` | **`0` = success**, anything else is a failure; read `Description` for detail |
| `LowProfileId` | The hosted-payment-page id; use with `TranzactionId` as the dedup key |
| `TranzactionId` | Cardcom transaction id |
| `ReturnValue` | Your own order id, echoed back. Not a status. |
| `TranzactionInfo` | Nested object carrying `CardOwnerIdentityNumber` (teudat zehut), `NumberOfPayments`, `ApprovalNumber` |
| `DocumentInfo` | Document details when Cardcom also issued the invoice |
There is no `DealResponse` field in v11 (zero occurrences in the spec), and `InternalDealNumber` / `CardOwnerID` / `NumOfPayments` are legacy classic-API names that do not appear in the result object. A dedup key built from `InternalDealNumber` is always `undefined`, so the duplicate-invoice guard never fires. Use `TranzactionId`.
Use the Cardcom API v11. `https://secure.cardcom.solutions/api/v11` is the **base path**, not a callable endpoint (it returns 404 on its own); append the operation, for example `POST https://secure.cardcom.solutions/api/v11/LowProfile/Create` to open a hosted payment page or `POST .../api/v11/Transactions/Transaction` for a direct charge. v11 also lets you register webhooks for document-creation events. URLs must be HTTPS and publicly routable (no `localhost`; use ngrok or Cloudflare Tunnel in dev). Full docs: `https://secure.cardcom.solutions/api/v11/DOCS`.
## Tranzila
Tranzila callbacks deliver GET parameters:
```
https://your-n8n.example.com/webhook/tranzila-callback?Response=000&index=12345&sum=100.00¤cy=1
```
`Response=000` is approved. Currency: `1` = ILS, `2` = USD, `3` = GBP, `7` = EUR. `Rone` = installments.
**Tranzila API v2** offers modern server-to-server (SAQ-D) plus iframe / hosted fields. Authentication uses an `X-tranzila-api-app-key` header (header confirmed via Stoplight API explorer at docs.tranzila.com). v2 supports Bit, tokenization, recurring billing, refunds, and 3D Secure (mandatory under SHVA rules). Prefer v2 over the legacy CGI pattern (`tranzila31.cgi`; the older `tranzila71dl.cgi` now 404s). Bit flow: server calls Tranzila v2, response includes a URL to embed in an iframe (QR code + phone push). See `https://docs.tranzila.com/` for the v2 documentation.
## Grow by Meshulam
Grow sends webhooks as POST. **Important:** the Grow API uses `multipart/form-data` (not JSON). After receiving a webhook, call `approveTransaction` to finalize the payment.
Webhook payload includes: `webhookKey`, `transactionCode`, `transactionType`, `asmachta` (transaction reference), `paymentSum`, `paymentDate`, `fullName`, `payerPhone`, `payerEmail`, `cardSuffix`, `cardBrand`, `paymentsNum`.
## Bit Payments
Bit is Israel's most popular mobile payment method, available through Tranzila (API v2) and Grow by Meshulam, not as a standalone API. Via Tranzila v2: create a payment page with `bit: true`; the customer scans a QR code or is redirected to Bit. Via Grow: enable Bit in the merchant dashboard; Bit transactions appear in the same webhook flow with a different `transactionType`.
---
# Israeli SMS Gateways (moved from SKILL.md Step 2)
## Israeli SMS Gateways
| Gateway | Host | Auth | Best For |
|---------|------|------|----------|
| 019 Telzar | `019sms.co.il` | Bearer token, or username + password | Bulk marketing, transactional |
| InforUMobile | `capi.inforu.co.il` | Bearer token (IP allowlist enforced) | OTP, transactional |
| Nexmo/Vonage IL | `rest.nexmo.com` | API key + secret | International + local |
019 Telzar example:
```
POST https://019sms.co.il/api
Headers: Authorization: Bearer {{$env.SMS_019_TOKEN}}
Body: { "from": "MyBusiness", "to": "{{$json.phone}}", "message": "{{$json.text}}" }
```
**019 returns HTTP 200 even when the send fails.** An auth failure comes back as `200 {"status":3,"message":"Username or password is incorrect or Expired and API token is invalid"}`, so an HTTP Request node with default settings treats it as success and the workflow continues with no SMS delivered. Enable **Always Output Data** on the node and branch on `$json.status` (0 = sent) with an IF node, not on the HTTP status code.
InforUMobile enforces an IP allowlist in addition to the token: a request from an unlisted IP returns `401 {"StatusId": -2, "StatusDescription": "Authentication failed or illegal IP address"}`. Whitelist your n8n egress IP in the InforU dashboard alongside Cardcom and Tranzila (Step 5).
Phone numbers must be international format `972XXXXXXXXX` (drop leading 0). Normalize in a Code node:
```javascript
const phone = $input.first().json.phone.replace(/[-\s]/g, '');
const formatted = phone.startsWith('0') ? '972' + phone.slice(1)
: phone.startsWith('+972') ? phone.slice(1) : phone;
return [{ json: { ...$input.first().json, phone: formatted } }];
```
## Before You Issue Anything: Three Israeli Rules That Gate the Whole Workflow
**1. Not every business may issue a tax invoice.** An עוסק פטור (osek patur) may not issue a חשבונית מס (types 305 / 320) and may not charge VAT at all; they issue a קבלה or a חשבונית עסקה. Ask the business's VAT status before choosing a document type, and never hardcode 305/320. Building an osek patur a workflow that charges 18% VAT makes them charge tax unlawfully and produces an invalid document. Zero-rated cases (exports, services to a foreign resident) and the Eilat free-trade-zone exemption also change the rate, so `vat_type` / `vatType` is a per-transaction decision, not a constant.
**2. Automated commercial messaging is regulated.** תיקון 40 לחוק התקשורת governs every SMS and WhatsApp send this skill can build: it requires prior consent from the recipient, identification of the sender, the word `פרסומת` on a commercial message, and a working opt-out in the same channel. Statutory damages reach 1,000 NIS per message with no proof of damage, which is exactly the exposure profile of a bulk loop. Meta's 24-hour window is a platform rule, not the legal one, and clearing it does not clear consent. Build the opt-out and the consent check into the workflow, not into a later phase.
**3. The data these workflows touch is regulated personal data.** Teudat zehut from a payment callback, live bank credentials, and Hebrew transaction descriptions piped into a third-party LLM all fall under the Privacy Protection Law as amended (Amendment 13, in force August 2025) and the 2017 security regulations. Minimise what you persist (do not store a teudat zehut you do not need), treat the Google Sheet as a regulated database with a retention rule, and do not send identifiable customer data to a foreign model without a lawful basis.
Also note the bookkeeping rules the API will not enforce for you: document numbering is sequential and immutable, a mistaken invoice is cancelled with a credit note (330) and never deleted, and the document itself must be retained for seven years, so persist the PDF rather than storing an expiring link.
---
# israeli-bank-scrapers in a Code Node (moved from SKILL.md Step 2)
## israeli-bank-scrapers via Code Node
n8n has no native Israeli bank node. Use a Code node to run `israeli-bank-scrapers` programmatically (it is a Node.js library, NOT a CLI). Requires Node.js >= 22.22.2.
Two n8n 2.x settings gate this (see Step 6). `NODE_FUNCTION_ALLOW_EXTERNAL=israeli-bank-scrapers` so `require()` resolves, and a working route to the secrets.
**The secret route needs care, because "use the credential store" is not implementable from inside a Code node.** The n8n Code node declares no credentials, so there is no `$credentials` to read there. `$env` is separately blocked by default in 2.x. That leaves three real options: set `N8N_BLOCK_ENV_ACCESS_IN_NODE=false` **on the task runner** and keep the `$env` pattern; or pass the secret in from a preceding credential-bearing node, accepting that it then appears in execution data; or use external secrets on an enterprise plan. Pick one deliberately. The variables below are placeholders for whichever route you chose, not globals that exist by themselves.
```javascript
const { createScraper, CompanyTypes } = require('israeli-bank-scrapers');
const scraper = createScraper({
companyId: CompanyTypes.hapoalim,
startDate: new Date('2026-01-01'),
combineInstallments: false,
showBrowser: false
});
// Hapoalim's login fields are userCode + password. Other banks differ, see below.
const password = BANK_PASS; // Hapoalim uses userCode + password
const result = await scraper.scrape({ userCode: BANK_USER, password });
if (!result.success) throw new Error(`${result.errorType}: ${result.errorMessage}`);
return result.accounts.flatMap(a => a.txns.map(txn => ({ json: txn })));
```
Supported scrapers (`CompanyTypes` members): hapoalim, leumi, discount, mercantile, mizrahi, otsarHahayal, beinleumi, union, massad, yahav, behatsdaa, beyahadBishvilha, oneZero, pagi, visaCal, max (formerly Leumi Card), isracard, amex.
**Login fields vary per bank.** There is no universal credential shape. See the per-bank table in `references/israeli-api-endpoints.md`; read `SCRAPERS[companyId].loginFields` before wiring credentials.
**Cloudflare blocking (2026):** Cloudflare's bot detection blocks headless browsers on Amex and Isracard. The maintained fork `@sergienko4/israeli-bank-scrapers` uses Camoufox as a workaround: `npm install @sergienko4/israeli-bank-scrapers`.
Store credentials in n8n's credential store, never in workflow JSON.
---
# Hebrew Data Handling in n8n (moved from SKILL.md Step 3)
## Handle Hebrew Data in n8n Nodes
n8n Code nodes process strings as UTF-8, so Hebrew works natively. Problems arise at boundaries (API responses, CSV exports, email templates):
| Issue | Where | Fix |
|-------|-------|-----|
| Reversed Hebrew in CSV | Spreadsheet File export | Set encoding to UTF-8-BOM |
| Broken nikud | HTTP Request response | Set response encoding to UTF-8 explicitly |
| Mixed RTL/LTR in emails | Send Email node | Wrap Hebrew in `<div dir="rtl">` |
| Hebrew JSON keys | data.gov.il responses | Normalize keys in Code node |
| Truncated Hebrew | String length checks | Use `Array.from(str).length`, not `.length` |
**NIS currency formatting:**
```javascript
new Intl.NumberFormat('he-IL', { style: 'currency', currency: 'ILS', minimumFractionDigits: 2 }).format(amount);
// 12345.60 -> 12,345.60 ₪
```
**Date parsing:** Israeli docs use DD/MM/YYYY. Morning API returns ISO 8601, but government datasets often return DD/MM/YYYY:
```javascript
function parseIsraeliDate(s) { const [d, m, y] = s.split('/').map(Number); return new Date(y, m - 1, d); }
const hebrewMonths = { 'ינואר': 0, 'פברואר': 1, 'מרץ': 2, 'אפריל': 3, 'מאי': 4, 'יוני': 5,
'יולי': 6, 'אוגוסט': 7, 'ספטמבר': 8, 'אוקטובר': 9, 'נובמבר': 10, 'דצמבר': 11 };
```
---
# שערי תשלום ישראליים (הועבר מ-SKILL.md שלב 5)
## Cardcom
Cardcom שולח POST עם נתונים בפורמט form-encoded:
שדות ה-callback לפי סכימת `LowProfileResult` ב-v11 (`https://secure.cardcom.solutions/swagger/v11/swagger.json`):
| שדה | תיאור |
|-----|-------|
| `ResponseCode` | **`0` = הצלחה**, כל ערך אחר הוא כישלון; הפירוט ב-`Description` |
| `Description` | תיאור התגובה |
| `LowProfileId` | מזהה דף התשלום המתארח |
| `TranzactionId` | מזהה העסקה ב-Cardcom, וזהו מפתח הדה-דופליקציה הנכון |
| `ReturnValue` | הערך שאתם עצמכם שלחתם, חוזר אליכם. **אינו סטטוס.** |
| `TranzactionInfo` | אובייקט מקונן: `CardOwnerIdentityNumber` (תעודת זהות), `NumberOfPayments`, `ApprovalNumber`, סכום |
| `DocumentInfo` | פרטי המסמך כאשר Cardcom הפיקה גם את החשבונית |
השדה `DealResponse` אינו קיים ב-v11 כלל, והשדות `InternalDealNumber` / `CardOwnerID` / `NumOfPayments` הם שמות של ה-API הישן שאינם מופיעים באובייקט התוצאה.
Code node לוולידציה אחרי ה-Webhook. שימו לב שהוא **אינו** מסיים את הבדיקה: אף שער ישראלי לא חותם על המטען, ולכן חובה לקרוא את העסקה מחדש מול Cardcom לפי `TranzactionId` לפני הפקת מסמך כלשהו.
```javascript
const data = $input.first().json.body ?? $input.first().json;
if (Number(data.ResponseCode) !== 0) {
return [{ json: { success: false, error: data.Description, lowProfileId: data.LowProfileId } }];
}
const info = data.TranzactionInfo ?? {};
return [{
json: {
success: true,
transactionId: data.TranzactionId, // מפתח הדה-דופליקציה
orderId: data.ReturnValue, // המזהה שלכם, לא סטטוס
installments: info.NumberOfPayments,
customerId: info.CardOwnerIdentityNumber
}
}];
```
**ממשק Cardcom API v11:** לאינטגרציות חדשות, מגדירים את ה-webhook URL דרך Cardcom API v11 במקום לוח הבקרה הישן. הכתובת `https://secure.cardcom.solutions/api/v11` היא נתיב בסיס ולא נקודת קצה שאפשר לקרוא לה (היא מחזירה 404 בפני עצמה), ולכן מוסיפים אחריה את הפעולה, למשל `POST https://secure.cardcom.solutions/api/v11/LowProfile/Create` לפתיחת דף תשלום מתארח או `POST .../api/v11/Transactions/Transaction` לחיוב ישיר. נקודת ה-v11 גם מאפשרת רישום webhooks לאירועי יצירת מסמכים (קבלות, חשבוניות) בנוסף לקריאות חיוב. ה-webhook חייב להיות HTTPS וזמין לאינטרנט (לא `localhost`, השתמשו ב-ngrok או Cloudflare Tunnel בפיתוח). תיעוד מלא: `https://secure.cardcom.solutions/api/v11/DOCS`.
## Tranzila
Tranzila משתמש בתבנית callback עם פרמטרי GET:
| שדה | תיאור | ערכים |
|-----|-------|-------|
| `Response` | קוד סטטוס | `000` = אושר, `001`-`999` = שגיאות |
| `index` | אינדקס עסקה | מספרי |
| `sum` | סכום שחויב | עשרוני (שקלים אם `currency=1`) |
| `currency` | קוד מטבע | `1` = ILS, `2` = USD, `3` = GBP, `7` = EUR |
| `Rone` | תשלומים | מספר |
**Tranzila API v2:** Tranzila מציעה אינטגרציית server-to-server (SAQ-D) פלוס iframe ושדות מתארחים לציות PCI. אימות דרך header בשם `X-tranzila-api-app-key` (לא Basic Auth, לא פרמטרי query). ה-v2 API תומך בתשלומי ביט, טוקניזציה, חיוב חוזר, החזרים, ו-3D Secure (חובה לכרטיסי אשראי ישראליים לפי כללי שב"א). לאינטגרציות חדשות, עדיף v2 על פני תבנית ה-CGI הישנה (`tranzila31.cgi`, שכן `tranzila71dl.cgi` כבר מחזיר 404). זרימת ביט: השרת קורא ל-Tranzila v2, התגובה כוללת URL להטמעה ב-iframe (שמציג קוד QR וטלפון להתראת push). תיעוד: `https://docs.tranzila.com/`.
## Grow by Meshulam
Grow by Meshulam שולח התראות webhook כבקשות POST. **חשוב:** ה-API של Grow משתמש ב-`multipart/form-data` לבקשות (לא JSON). אחרי קבלת webhook, חובה לקרוא ל-`approveTransaction` כדי לסיים את העסקה.
שדות ב-webhook payload:
| שדה | תיאור |
|-----|-------|
| `webhookKey` | מפתח אימות webhook |
| `transactionCode` | קוד עסקה ייחודי |
| `transactionType` | סוג העסקה |
| `asmachta` | מספר אסמכתא |
| `paymentSum` | סכום שחויב |
| `paymentDate` | תאריך התשלום |
| `fullName` | שם מלא של הלקוח |
| `payerPhone` | טלפון הלקוח |
| `payerEmail` | אימייל הלקוח |
| `cardSuffix` | 4 ספרות אחרונות של הכרטיס |
| `cardBrand` | מותג הכרטיס (Visa, Mastercard וכו') |
| `paymentsNum` | מספר תשלומים |
Code node לעיבוד webhook של Grow ואישור:
```javascript
const data = $input.first().json;
const payment = {
transactionCode: data.transactionCode,
asmachta: data.asmachta,
amount: parseFloat(data.paymentSum),
customerName: data.fullName,
customerPhone: data.payerPhone,
customerEmail: data.payerEmail,
installments: parseInt(data.paymentsNum) || 1
};
// חובה לקרוא ל-approveTransaction אחרי קבלת ה-webhook
// זה נעשה ב-HTTP Request node הבא עם multipart/form-data
return [{ json: payment }];
```
**רשימת IP לבנה:** Cardcom ו-Tranzila דורשים שה-IP של שרת ה-webhook יהיה ברשימה המורשית בלוח הבקרה שלהם. באירוח עצמי השתמשו ב-IP קבוע או reverse proxy עם כתובת יציאה קבועה.
## תשלומי ביט
ביט הוא אמצעי התשלום הנייד הפופולרי ביותר בישראל. תשלומי ביט זמינים דרך Tranzila (API v2) ו-Grow by Meshulam, לא כ-API עצמאי.
ביט דרך Tranzila v2: יוצרים דף תשלום עם `bit: true` בבקשה. הלקוח סורק QR או מופנה לביט. ה-webhook callback משתמש באותם שדות כמו עסקאות כרטיס אשראי.
ביט דרך Grow by Meshulam: מפעילים ביט בלוח הבקרה של Grow. עסקאות ביט מופיעות באותו תהליך webhook כמו עסקאות כרטיס, עם ערך `transactionType` שונה.
---
# טיפול בנתונים בעברית ב-n8n (הועבר מ-SKILL.md שלב 3)
## טקסט RTL ב-Code Nodes
ב-n8n צמתי Code מעבדים מחרוזות כ-UTF-8, אז עברית עובדת באופן טבעי. הבעיות מופיעות בממשקים: תגובות API, ייצוא CSV, תבניות מייל.
| בעיה | איפה קורה | פתרון |
|------|-----------|-------|
| עברית הפוכה ב-CSV | ייצוא Spreadsheet File node | הגדרת encoding ל-UTF-8-BOM |
| ניקוד שבור | פרסור תגובת HTTP Request | הגדרת encoding ל-UTF-8 מפורשות |
| ערבוב RTL/LTR במיילים | Send Email node | עטיפת טקסט עברי ב-`<div dir="rtl">` |
| מפתחות JSON בעברית | תגובות data.gov.il | נרמול מפתחות ב-Code node |
| עברית קטועה | בדיקות אורך מחרוזת | שימוש ב-`Array.from(str).length` במקום `.length` |
## פורמט מטבע שקלים
Code node לעיצוב סכומים בשקלים:
```javascript
function formatNIS(amount) {
return new Intl.NumberFormat('he-IL', {
style: 'currency',
currency: 'ILS',
minimumFractionDigits: 2
}).format(amount);
}
// קלט: 12345.60
// פלט: 12,345.60 ₪
```
**לגבי Morning API:** סכומים ב-API הם בשקלים עשרוניים (לא אגורות). `price: 50` זה 50.00 ש"ח. אין צורך להמיר אגורות לשקלים כשעובדים עם Morning API.
## פרסור תאריכים ישראליים
מסמכים ישראליים משתמשים בפורמט DD/MM/YYYY. חשוב לפרסר נכון:
```javascript
// פרסור תאריך ישראלי DD/MM/YYYY
function parseIsraeliDate(dateStr) {
const [day, month, year] = dateStr.split('/').map(Number);
return new Date(year, month - 1, day);
}
// פרסור שמות חודשים בעברית (נפוץ במסמכי ממשלה)
const hebrewMonths = {
'ינואר': 0, 'פברואר': 1, 'מרץ': 2, 'אפריל': 3,
'מאי': 4, 'יוני': 5, 'יולי': 6, 'אוגוסט': 7,
'ספטמבר': 8, 'אוקטובר': 9, 'נובמבר': 10, 'דצמבר': 11
};
```
---
# israeli-bank-scrapers ב-Code Node (הועבר מ-SKILL.md שלב 2)
## israeli-bank-scrapers דרך Code Node
ל-n8n אין node מובנה לבנקים ישראליים. משתמשים ב-Code node להרצת `israeli-bank-scrapers` בצורה פרוגרמטית. החבילה היא ספריית Node.js (לא כלי CLI), לכן חייבים להשתמש ב-`createScraper()`:
**חשוב:** דורש Node.js >= 22.22.2 בסביבת n8n.
שתי הגדרות ב-n8n 2.x חייבות להיות במקום לפני שהקוד הזה רץ בכלל (פירוט בשלב 6): הגדרת `NODE_FUNCTION_ALLOW_EXTERNAL=israeli-bank-scrapers` כדי ש-`require()` יצליח, ושליפת פרטי ההתחברות מ-credential store במקום מ-`$env` (גישה למשתני סביבה מתוך Code node חסומה כברירת מחדל ב-2.x).
```javascript
// ב-Code node (ב-n8n 2.0: רץ ב-task runner מבודד)
const { createScraper, CompanyTypes } = require('israeli-bank-scrapers');
const scraper = createScraper({
companyId: CompanyTypes.hapoalim,
startDate: new Date('2026-01-01'),
combineInstallments: false,
showBrowser: false
});
// שדות ההתחברות של הפועלים הם userCode ו-password. בבנקים אחרים השדות שונים.
const credentials = {
userCode: BANK_USER,
password
};
const result = await scraper.scrape(credentials);
if (result.success) {
return result.accounts.flatMap(account =>
account.txns.map(txn => ({ json: txn }))
);
} else {
throw new Error(`Scraping failed: ${result.errorType} - ${result.errorMessage}`);
}
```
סורקים נתמכים (חברי ה-enum בשם `CompanyTypes`): הפועלים (hapoalim), לאומי (leumi), דיסקונט (discount), מרכנתיל (mercantile), מזרחי (mizrahi), אוצר החייל (otsarHahayal), בינלאומי (beinleumi), יוניון (union), מסד (massad), יהב (yahav), בהצדעה (behatsdaa), ביחד בשבילה (beyahadBishvilha), oneZero, פאג"י (pagi), ויזה כאל (visaCal), מקס (max, לשעבר לאומי קארד), ישראכרט (isracard), אמקס (amex).
**שדות ההתחברות שונים מבנק לבנק.** אין מבנה אחיד לפרטי ההתחברות. בדקו את `SCRAPERS[companyId].loginFields` לפני שמחברים credentials:
| בנק | שדות נדרשים |
|------|-------------|
| הפועלים | `userCode`, `password` |
| לאומי, מזרחי, אוצר החייל, מקס, ויזה כאל, יוניון, בינלאומי, מסד, יהב | `username`, `password` |
| דיסקונט, מרכנתיל | `id`, `password`, `num` |
| ישראכרט, אמקס | `id`, `card6Digits`, `password` |
**חסימת Cloudflare (2026):** מתחילת 2026, Cloudflare חוסם דפדפנים headless באתרי אמקס וישראכרט. הפורק המתוחזק `@sergienko4/israeli-bank-scrapers` משתמש ב-Camoufox כפתרון עוקף. אם נתקלים בכשלונות סריקה מתמשכים עם ספקים אלה:
```bash
npm install @sergienko4/israeli-bank-scrapers
```
**אבטחה:** פרטי התחברות נשמרים ב-credential store של n8n, לא בתוך ה-workflow JSON. משתמשים במשתני סביבה לערכים רגישים.
---
# שערי SMS ישראליים (הועבר מ-SKILL.md שלב 2)
## שערי SMS ישראליים
| שער | שרת | אימות | מתאים ל |
|-----|------|-------|---------|
| 019 Telzar | `019sms.co.il` | Bearer token, או שם משתמש וסיסמה | שיווק המוני, הודעות עסקיות |
| InforUMobile | `capi.inforu.co.il` | Bearer token (עם רשימת IP מורשים) | OTP, הודעות עסקיות |
| Nexmo/Vonage IL | `rest.nexmo.com` | API key + secret | בינלאומי + מקומי |
דוגמת 019 Telzar SMS ב-HTTP Request node:
```
Method: POST
URL: https://019sms.co.il/api
Headers:
Content-Type: application/json
Authorization: Bearer {{$env.SMS_019_TOKEN}}
Body:
{
"from": "MyBusiness",
"to": "{{$json.phone}}",
"message": "{{$json.text}}"
}
```
**שער 019 מחזיר HTTP 200 גם כשהשליחה נכשלת.** כשל אימות חוזר כ-`200 {"status":3,"message":"Username or password is incorrect or Expired and API token is invalid"}`, כך ש-HTTP Request node בהגדרות ברירת המחדל מפרש את זה כהצלחה וה-workflow ממשיך בלי שנשלחה שום הודעה. מפעילים **Always Output Data** על ה-node ומסתעפים לפי `$json.status` (הערך 0 מציין שליחה מוצלחת) ב-IF node, ולא לפי קוד ה-HTTP.
שער InforUMobile אוכף רשימת IP מורשים בנוסף לטוקן. פנייה מכתובת שאינה ברשימה מקבלת `401 {"StatusId": -2, "StatusDescription": "Authentication failed or illegal IP address"}`. מוסיפים את כתובת ה-IP היוצאת של n8n לרשימה בלוח הבקרה של InforU, לצד Cardcom ו-Tranzila (שלב 5).
פורמט מספרי טלפון ישראליים: תמיד שולחים בפורמט בינלאומי `972XXXXXXXXX` (מורידים את ה-0 הפותח). Code node לפני ה-SMS node מטפל בזה:
```javascript
const phone = $input.first().json.phone;
const cleaned = phone.replace(/[-\s]/g, '');
const formatted = cleaned.startsWith('0')
? '972' + cleaned.slice(1)
: cleaned.startsWith('+972')
? cleaned.slice(1)
: cleaned;
return [{ json: { ...$input.first().json, phone: formatted } }];
```
---
# WhatsApp Business Cloud in n8n (detail)
## WhatsApp Business Cloud
WhatsApp is the dominant Israeli business messaging channel and n8n ships first-class nodes: **`n8n-nodes-base.whatsApp`** ("WhatsApp Business Cloud", typeVersion 1.1, credential `whatsAppApi`) for sending, and **`n8n-nodes-base.whatsAppTrigger`** for inbound messages. Message operations are Send, Send Template, and Send and Wait for Response; message types cover text, image, document, audio, video, location and contacts. A separate Media resource handles upload / URL-get / delete.
Two Meta platform rules break these workflows in production, and neither is an n8n concern:
- **The 24-hour customer service window.** A user messaging (or calling) your number opens a 24-hour window; inside it you may send free-form messages. Outside it, only a pre-approved template will deliver. A scheduled Israeli workflow, by definition, fires outside the window, so it must use Send Template.
- **Templates need prior approval in one of three categories:** Marketing, Utility, Authentication. Approval takes real time and Hebrew templates are reviewed like any other, so approve the template before the workflow ships, not on launch day.
Pricing is per delivered message (conversation-based pricing was replaced on July 1, 2025), so a fan-out loop costs money per item. Rate-limit deliberately.
---
# WhatsApp Business Cloud ב-n8n (פירוט)
## WhatsApp Business Cloud
וואטסאפ הוא ערוץ ההודעות העסקי הדומיננטי בישראל, ול-n8n יש צמתים ייעודיים: **`n8n-nodes-base.whatsApp`** ("WhatsApp Business Cloud", typeVersion 1.1, credential בשם `whatsAppApi`) לשליחה, ו-**`n8n-nodes-base.whatsAppTrigger`** לקליטת הודעות נכנסות. פעולות ההודעה הן Send, Send Template ו-Send and Wait for Response, וסוגי ההודעה כוללים טקסט, תמונה, מסמך, אודיו, וידאו, מיקום ואנשי קשר. משאב Media נפרד מטפל בהעלאה, קבלת URL ומחיקה.
שני כללים של מטא שוברים את התהליכים האלה בפרודקשן, ושניהם לא בשליטת n8n:
- **חלון שירות הלקוחות של 24 שעות.** משתמש ששולח לכם הודעה (או מתקשר) פותח חלון של 24 שעות שבתוכו אפשר לשלוח הודעות חופשיות. מחוצה לו רק תבנית מאושרת מראש תגיע ליעד. תהליך מתוזמן ישראלי, מעצם הגדרתו, רץ מחוץ לחלון, ולכן חייב להשתמש ב-Send Template.
- **תבניות דורשות אישור מראש באחת משלוש קטגוריות:** Marketing, Utility, Authentication. האישור לוקח זמן אמיתי ותבניות בעברית נבדקות כמו כל תבנית אחרת, אז אשרו את התבנית לפני שהתהליך עולה לאוויר ולא ביום ההשקה.
התמחור הוא לפי הודעה שנמסרה (התמחור לפי שיחה הוחלף ב-1.7.2025), ולכן לולאת פיזור עולה כסף על כל פריט. הגבילו קצב במכוון.
references/n8n-version-migration.md
# n8n Version Migration: 2.x Security Line, 3.0 Readiness, Israeli Hosting
Companion to `SKILL.md` Step 6. Everything here is version-sensitive; re-check the npm
dist-tags (`https://registry.npmjs.org/-/package/n8n/dist-tags`) and the advisory feed
(`https://api.github.com/advisories?ecosystem=npm&affects=n8n`) before trusting a pin.
## n8n 2.x Security Patches and Breaking Changes
n8n 2.0 shipped in December 2025; current stable is 2.38.7 as of September 2026 (beta on 2.39.x, new minor most weeks). Pin a specific tag in production, never `n8nio/n8n:latest`.
**CRITICAL security patch (pin >= 2.32.1).** Three CRITICAL vulnerabilities were disclosed 2026-05-14 and patched in 2.22.1:
| CVE | GHSA | Impact |
|-----|------|--------|
| CVE-2026-44789 | GHSA-c8xv-5998-g76h | HTTP Request node pagination prototype pollution **to RCE** |
| CVE-2026-44790 | GHSA-57g9-58c2-xjg3 | Arbitrary file read via Git node |
| CVE-2026-44791 | GHSA-wrwr-h859-xh2r | XML node prototype pollution patch bypass |
CVE-2026-44789 sits on this skill's critical path: every Israeli integration here is an HTTP Request node. Further HIGH-severity fixes landed through 2.31.5 and 2.32.1 (credential exfiltration via shared workflows, cross-tenant credential takeover, expression sandbox escapes), so **2.32.1 is the practical minimum**. No advisory published since patches above 2.32.1, so the floor is unchanged; 2.38.7 is the current stable and is what you should actually run.
Two earlier issues are often cited and frequently misdescribed:
- **CVE-2026-21858 ("Ni8mare", CVSS 10.0), published 2026-01-07** is unauthenticated **file access** via improper webhook request handling, not RCE. It affects 1.65.0 through 1.120.x only and was patched in 1.121.0. **The 2.x line was never affected**, so it is not a reason to pin any 2.x version.
- **CVE-2026-27493 (CVSS 9.5) + CVE-2026-27577 (CVSS 9.4), published 2026-02-25** are the pair that motivates the 2.10.1 floor: unauthenticated expression injection via Form nodes plus an expression sandbox escape to RCE, affecting <1.123.22, 2.0.0-2.9.2 and 2.10.0, patched in 1.123.22 / 2.9.3 / 2.10.1. That floor is now superseded by the 2.32.1 requirement above.
Any public Webhook node (every payment-gateway workflow in this skill) widens the exposure of all of these.
Key n8n 2.0 changes affecting Israeli workflows:
| Change | Impact | Action |
|--------|--------|--------|
| Code-node `$env` access blocked by default | Every `$env.BANK_PASS` / `$env.WEBHOOK_HMAC_SECRET` read returns nothing | Set `N8N_BLOCK_ENV_ACCESS_IN_NODE=false` on the runner, or pass the secret in from a preceding node. NOT the credential store: the Code node declares none |
| `require()` restricted in Code nodes | `require('crypto')` and `require('israeli-bank-scrapers')` both fail | Set `NODE_FUNCTION_ALLOW_BUILTIN` and `NODE_FUNCTION_ALLOW_EXTERNAL` |
| Execute Command node disabled by default | Bank-scraper workflows using Execute Command break | Use Code node, or re-enable via `NODES_EXCLUDE` |
| Save/Publish model | Workflows must be explicitly published | Publish after import or creation |
| Task runner isolation for Code nodes | Code runs in isolated sandboxes | Set the module vars on the **runner**, not the main container |
| Python Code node rebuilt on task runners | Pyodide-based Python removed; native Python needs runners in external mode | Set up external-mode task runners, or use JavaScript |
| MySQL/MariaDB support removed | Cannot use them as n8n backend DB | Migrate to PostgreSQL or SQLite |
**Code node module access.** n8n disables `require()` for external modules unless the variable is set, and this skill needs both:
```
NODE_FUNCTION_ALLOW_BUILTIN=crypto
NODE_FUNCTION_ALLOW_EXTERNAL=israeli-bank-scrapers
```
Without the first, the HMAC webhook verifier in `references/webhook-auth-patterns.md` throws on `require('crypto')`. Without the second, the bank-scraper Code node throws on `require('israeli-bank-scrapers')`. **If task runners are enabled (the default in 2.0), set these on the task runner, not on the main n8n container** or they have no effect.
**Code node secrets.** `N8N_BLOCK_ENV_ACCESS_IN_NODE` defaults to `true` in 2.x, so `$env.*` inside a Code node returns nothing. The credential store cannot substitute, because the Code node's node description declares no credentials, so there is no `$credentials` there. Either set `N8N_BLOCK_ENV_ACCESS_IN_NODE=false` on the task runner, pass the secret in from a preceding credential-bearing node (accepting that it lands in execution data), or use external secrets on enterprise.
To re-enable Execute Command, override `NODES_EXCLUDE` so it no longer contains `n8n-nodes-base.executeCommand` (empty list works), then restart n8n:
```
NODES_EXCLUDE="[]"
```
There is no `N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE` variable (a common hallucination). Enabling Execute Command lets anyone with workflow edit access run arbitrary shell, so use only in trusted single-user deployments. Code nodes remain the recommended path.
## Preparing for n8n 3.0 (October 2026)
n8n 3.0 is scheduled for October 2026 and its breaking-changes page is already published. Four items hit this skill directly:
- **Self-hosting will require Docker.** `npm` / `npx n8n` installs will no longer be supported, so the Compose file below becomes the only supported shape.
- **The Function, Function Item and Item Lists nodes are removed.** Older Israeli workflows built before the Code node will not import. Migrate Function to Code, and Item Lists to Split Out / Aggregate / Sort / Limit / Remove Duplicates / Summarize.
- **The `$getPairedItem` expression helper is removed.** Use `pairedItem` or `$("<node name>").item`.
- **AI Agent node v1 is removed**, along with the SQL, Conversational, OpenAI Functions, Plan-and-Execute and ReAct agent modes. Everything in Step 7 should be built on the current Tools Agent, not on a legacy mode.
Also removed: workflow import from URL in the editor, and the Compression node limits drop sharply. Audit for these before upgrading, not after.
## Israeli Cloud Options
| Provider | Data Residency | Notes |
|----------|---------------|-------|
| AWS (il-central-1) | Israel (Tel Aviv) | Full Docker support, region GA |
| Azure (Israel Central) | Israel | `israelcentral` region |
| Google Cloud (me-west1) | Israel (Tel Aviv) | Launched 2022 |
| Kamatera | Israel (Petah Tikva) | VPS + Docker, Israeli company, NIS billing |
| ActiveCloud / HQserv / MedOne | Israel | VPS + Docker, Hebrew support |
Israel's Privacy Protection Authority (PPA) does not mandate that all data stay in Israel, but restricts transfers to countries without adequate data protection. For workflows processing PII (teudat zehut, bank, medical), choose an Israeli DC or verify destination adequacy on the PPA's approved list.
## Docker Compose for Self-Hosted n8n
```yaml
services:
n8n:
# Must be >= 2.32.1 for the CVE-2026-44789/44790/44791 critical chain. 2.38.7 is current stable.
image: n8nio/n8n:2.38.7
restart: unless-stopped
ports: ["5678:5678"]
environment:
- N8N_HOST=${N8N_HOST}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://${N8N_HOST}/
- GENERIC_TIMEZONE=Asia/Jerusalem
- TZ=Asia/Jerusalem
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:
```
Notes:
- n8n 1.0+ uses built-in user management; old `N8N_BASIC_AUTH_*` vars are removed. n8n prompts for an owner account on first launch.
- Set both `GENERIC_TIMEZONE=Asia/Jerusalem` AND `TZ=Asia/Jerusalem`. Without these, Schedule Trigger nodes default to UTC and Shabbat calculations drift 2-3 hours. Israeli DST runs Friday-before-last-Sunday-of-March through last Sunday of October.
- Never run `:latest` in production after the 2026 CVE chain. Pin the tag and update via controlled redeploy.
- If Code nodes need `require()` or `$env`, add `NODE_FUNCTION_ALLOW_BUILTIN`, `NODE_FUNCTION_ALLOW_EXTERNAL` and (only if needed) `N8N_BLOCK_ENV_ACCESS_IN_NODE=false` to the task-runner service rather than here.
---
# גרסאות n8n: קו האבטחה של 2.x, היערכות ל-3.0, ואירוח בישראל
## שינויים משמעותיים ב-n8n 2.0 (דצמבר 2025) + עדכוני אבטחה (2026)
גרסה n8n 2.0 שוחררה בדצמבר 2025; הגרסה היציבה הנוכחית היא 2.38.7 נכון לספטמבר 2026 (בטא על 2.39.x, עם minor חדש כמעט כל שבוע). נעלו תג ספציפי בפרודקשן במקום `n8nio/n8n:latest`.
**טלאי אבטחה קריטי, חובה לעבור ל-2.32.1 לפחות.** שלוש חולשות ברמת CRITICAL פורסמו ב-14.5.2026 ותוקנו ב-2.22.1:
| CVE | GHSA | השפעה |
|-----|------|-------|
| CVE-2026-44789 | GHSA-c8xv-5998-g76h | זיהום prototype במנגנון ה-pagination של HTTP Request node, שמוביל ל-RCE |
| CVE-2026-44790 | GHSA-57g9-58c2-xjg3 | קריאת קבצים שרירותית דרך צומת Git |
| CVE-2026-44791 | GHSA-wrwr-h859-xh2r | עקיפת הטלאי לזיהום prototype בצומת XML |
חולשת CVE-2026-44789 נמצאת בדיוק על הנתיב הקריטי של הסקיל הזה, מפני שכל אינטגרציה ישראלית כאן היא HTTP Request node. תיקונים נוספים ברמת HIGH נחתו לאורך 2.31.5 ו-2.32.1 (דליפת credentials דרך workflows משותפים, השתלטות על credentials בין דיירים, ובריחה מארגז החול של הביטויים), ולכן **2.32.1 היא רצפת המינימום המעשית** ו-2.38.7 היא הגרסה היציבה הנוכחית.
שתי חולשות מוקדמות יותר מצוטטות הרבה ולרוב מתוארות לא נכון:
- **חולשת CVE-2026-21858 ("Ni8mare", CVSS 10.0), שפורסמה ב-7.1.2026,** היא **גישה לקבצים** ללא אימות דרך טיפול שגוי בבקשות webhook, ולא RCE. היא נוגעת לגרסאות 1.65.0 עד 1.120.x בלבד ותוקנה ב-1.121.0. **קו 2.x מעולם לא היה פגיע**, ולכן היא אינה סיבה לנעול גרסת 2.x כלשהי.
- **חולשות CVE-2026-27493 (CVSS 9.5) ו-CVE-2026-27577 (CVSS 9.4), שפורסמו ב-25.2.2026,** הן הצמד שממנו נגזרה רצפת 2.10.1: הזרקת ביטויים ללא אימות דרך צמתי Form, ובריחה מארגז החול של הביטויים עד כדי RCE. הן נוגעות לגרסאות 1.123.22>, 2.0.0 עד 2.9.2, ו-2.10.0, ותוקנו ב-1.123.22 / 2.9.3 / 2.10.1. הרצפה הזו מוחלפת כעת בדרישת 2.32.1.
כל Webhook ציבורי (כל זרימת תשלום בסקיל הזה חושפת אחד) מרחיב את החשיפה לכל אלה. נעלו את התג ב-`docker-compose.yml` ועקבו אחרי פיד האבטחה של n8n.
n8n 2.0 הביא שינויים משמעותיים שמשפיעים על תהליכים ישראליים:
| שינוי | השפעה | פעולה נדרשת |
|-------|-------|------------|
| גישה ל-`$env` מתוך Code node חסומה כברירת מחדל | כל קריאת `$env.BANK_PASS` או `$env.WEBHOOK_HMAC_SECRET` מחזירה ריק | הגדרת `N8N_BLOCK_ENV_ACCESS_IN_NODE=false` על ה-runner, או העברת הסוד מצומת קודם. לא credential store: ל-Code node אין כזה |
| שימוש ב-`require()` מוגבל ב-Code nodes | גם `require('crypto')` וגם `require('israeli-bank-scrapers')` נכשלים | הגדרת `NODE_FUNCTION_ALLOW_BUILTIN` ו-`NODE_FUNCTION_ALLOW_EXTERNAL` |
| Execute Command node מושבת כברירת מחדל | תהליכי סריקת בנקים שמשתמשים ב-Execute Command ישברו | מעבר ל-Code node (שלב 2), או הפעלה מחדש דרך משתנה הסביבה `NODES_EXCLUDE` (ראו למטה) |
| מודל שמירה/פרסום | תהליכים חייבים להתפרסם מפורשות כדי לפעול | פרסום תהליכים אחרי ייבוא או יצירה |
| בידוד task runner ל-Code nodes | Code nodes רצים ב-sandbox מבודד | הגדרת משתני המודולים על ה-**task runner**, לא על הקונטיינר הראשי |
| צומת Python נבנה מחדש על task runners | Python מבוסס Pyodide הוסר, Python נייטיב דורש runners במצב external | הקמת task runners במצב external, או שימוש ב-JavaScript |
| הסרת תמיכה ב-MySQL/MariaDB | לא אפשר להשתמש ב-MySQL/MariaDB כ-DB backend | מעבר ל-PostgreSQL (מומלץ) או SQLite |
**גישה למודולים ב-Code node.** מנגנון `require()` למודולים חיצוניים מושבת ב-n8n אלא אם המשתנה מוגדר, והסקיל הזה זקוק לשניהם:
```
NODE_FUNCTION_ALLOW_BUILTIN=crypto
NODE_FUNCTION_ALLOW_EXTERNAL=israeli-bank-scrapers
```
בלי הראשון, מאמת ה-HMAC ב-`references/webhook-auth-patterns.md` זורק שגיאה על `require('crypto')`. בלי השני, ה-Code node של סריקת הבנק זורק שגיאה על `require('israeli-bank-scrapers')`. **אם task runners פעילים (ברירת המחדל ב-2.0), מגדירים את המשתנים על ה-task runner ולא על הקונטיינר הראשי של n8n**, אחרת אין להם שום השפעה.
**סודות ב-Code node.** המשתנה `N8N_BLOCK_ENV_ACCESS_IN_NODE` מוגדר כ-`true` כברירת מחדל ב-2.x, ולכן `$env.*` בתוך Code node מחזיר ריק. עדיף להעביר את סודות הבנק וה-HMAC ל-credential store, וזו גם ההמלצה של n8n עצמה בתיעוד השינוי. מגדירים `N8N_BLOCK_ENV_ACCESS_IN_NODE=false` רק אם חייבים לשמר את דפוס ה-`$env`.
ב-n8n 2.0, צומת Execute Command (וגם Local File Trigger) נוסף לרשימת `NODES_EXCLUDE` של ברירת המחדל, ולכן הוא נעלם מלוח הצמתים. כדי להפעיל מחדש את Execute Command, דורסים את `NODES_EXCLUDE` כך שלא יכיל את `n8n-nodes-base.executeCommand`, הדריסה הפשוטה ביותר היא רשימה ריקה, ואז מפעילים מחדש את n8n:
```
NODES_EXCLUDE="[]"
```
לפי תיעוד השינויים של n8n 2.0 זה המנגנון הנתמך, אין משתנה `N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE`. הפעלת Execute Command מאפשרת לכל מי שיש לו הרשאת עריכת workflow להריץ פקודות shell שרירותיות, אז עשו זאת רק בסביבות מהימנות וחד-משתמש. הגישה המומלצת נשארת מעבר ל-Code nodes.
## אפשרויות ענן ישראליות
| ספק | מיקום נתונים | תמיכה ב-n8n | הערות |
|-----|-------------|-------------|-------|
| AWS (il-central-1) | ישראל (תל אביב) | Docker מלא | אזור מלא זמין |
| Azure (Israel Central) | ישראל | Docker מלא | אזור israelcentral |
| Google Cloud (me-west1) | ישראל (תל אביב) | Docker מלא | הושק 2022 |
| Kamatera | ישראל (פתח תקווה) | VPS עם Docker | חברה ישראלית, חיוב בשקלים |
| ActiveCloud / HQserv / MedOne | ישראל | VPS עם Docker | חברות ישראליות, תמיכה מקומית בעברית |
**ציות לרגולציית מיקום נתונים:** הרשות להגנת הפרטיות (PPA) לא דורשת שכל המידע יישאר בישראל. היא מגבילה העברת מידע אישי למדינות ללא הגנה מספקת, או דורשת אמצעי הגנה נוספים (כמו סעיפים חוזיים). לתהליכים שמעבדים מידע אישי (תעודות זהות, פרטי בנק, מידע רפואי), יש לבחור ספק עם מרכז נתונים בישראל או לוודא שמדינת היעד ברשימה המאושרת של הרשות.
## Docker Compose לאירוח עצמי
```yaml
services:
n8n:
# נועלים תג ספציפי. אסור :latest בפרודקשן.
# חייב להיות לפחות 2.32.1 כדי להיות מטולא נגד שרשרת CVE-2026-44789/44790/44791.
image: n8nio/n8n:2.38.7
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_HOST=${N8N_HOST}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://${N8N_HOST}/
- GENERIC_TIMEZONE=Asia/Jerusalem
- TZ=Asia/Jerusalem
# מומלץ: רוטציה של מפתח ההצפנה רק דרך תהליך המיגרציה המתועד.
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:
```
**הערות:**
- n8n 1.0 ומעלה משתמש בניהול משתמשים מובנה (אימייל + סיסמה). משתני הסביבה הישנים `N8N_BASIC_AUTH_*` הוסרו. בהפעלה ראשונה, n8n מבקש ליצור חשבון בעלים.
- `version: '3.8'` לא מופיע כי הוא מיושן ב-Docker Compose V2.
- **קריטי:** חובה להגדיר `GENERIC_TIMEZONE=Asia/Jerusalem` ו-`TZ=Asia/Jerusalem`. בלי זה, כל ה-Schedule Trigger nodes רצים לפי UTC, וחישובי שבת יהיו מוזזים ב-2-3 שעות (ישראל ב-UTC+2 בחורף, UTC+3 בקיץ). שעון קיץ בישראל מתחיל ביום שישי שלפני יום ראשון האחרון של מרץ ומסתיים ביום ראשון האחרון של אוקטובר.
---
references/n8n-workflow-authoring.md
# n8n Workflow Authoring: Platform Choice, JSON Shape, Credentials
Companion to `SKILL.md` Steps 8-10.
## When to Use n8n vs Alternatives
| Criteria | n8n | Make.com | Zapier |
|----------|-----|----------|--------|
| Self-hosting (data residency) | Yes (Docker) | No | No |
| Israeli API nodes | None built-in, use HTTP/Code | Some community | Very few |
| Workflow limit | Unlimited (self-hosted) | Plan-based | Plan-based |
| Code execution | Full JS/Python | Limited JS | Limited |
| AI Agent nodes | 70+ AI, MCP support | AI features | AI features |
| Hebrew UI | No | Partial | No |
Choose n8n for Israeli data residency, unlimited automations, or full code access for Israeli API quirks (Hebrew encoding, phone formatting, VAT, allocation numbers).
## Workflow JSON Import/Export
n8n workflows are JSON documents. Agents building them programmatically need the shape:
```json
{
"name": "Morning daily reconciliation",
"nodes": [{
"parameters": { "rule": { "interval": [{ "field": "cronExpression", "expression": "0 6 * * 0-4" }] } },
"name": "Schedule Trigger", "type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2, "position": [240, 300]
}],
"connections": {
"Schedule Trigger": { "main": [[{ "node": "Get Token", "type": "main", "index": 0 }]] }
}
}
```
- **`nodes`**: each has unique `name` (used as connection key), `type` (e.g. `n8n-nodes-base.httpRequest`), `typeVersion` (must match a version n8n supports), `parameters`, and `position`.
- **`connections`**: keyed by source node name, mapping `main` output to an array of arrays of `{ node, type, index }` targets (double array allows multiple outputs, e.g. IF branches).
- Export via `GET /api/v1/workflows/{id}`, import via `POST /api/v1/workflows`. After importing into n8n 2.0 you must **publish** before it runs.
## Credentials Setup for Israeli APIs
n8n stores secrets in its encrypted credential store, never inline in workflow JSON:
- **Morning (Green Invoice) JWT**: no native credential. Chain HTTP Request nodes; the first calls `/account/token`, later nodes send `Authorization: Bearer {{token}}` via Header Auth or an expression. Token expires after 60 minutes, so refresh per execution.
- **Israeli SMS gateways (019, InforUMobile)**: Header Auth credential, name `Authorization`, value `Bearer <token>`.
- **Payment gateways (Cardcom, Tranzila, Grow)**: store merchant IDs / API keys as Generic Credential, referenced via `{{$credentials.fieldName}}`. Grow's `multipart/form-data` requests still pull secrets from the credential.
- For self-hosted n8n, set a stable `N8N_ENCRYPTION_KEY` so the credential store survives restarts.
---
# n8n AI Agent Nodes for Israeli Workflows
n8n 2.x ships native LangChain integration (the "Advanced AI" node group): 70+ AI nodes including Tools Agent, Conversational Agent, Memory (Window/Summary Buffer), Vector Store nodes (Pinecone, Qdrant, Supabase pgvector for RAG), and Model nodes for OpenAI, Anthropic and local models via Ollama.
**Do not hardcode a model name from a tutorial.** The current Anthropic Chat Model node resolves its model list dynamically from the Anthropic API at runtime rather than from a fixed dropdown, so the list moves without an n8n release. Pick by requirement, then take whatever the node offers today:
| Use case | How to choose | Why |
|----------|---------------|-----|
| Hebrew transaction categorization | Current mid-tier frontier model (the node's own default is the newest Sonnet) | Strong Hebrew at low cost; the task is classification, not reasoning |
| Hebrew document summarization | Current top-tier model | Long Hebrew legal text rewards reasoning depth |
| Real-time Hebrew chat | Lowest-latency model in the same family | Short Hebrew turns are latency-bound, not quality-bound |
| On-prem / data residency | Ollama with a multilingual local model on an Israeli VPS | PII stays in Israel; acceptable for categorization |
**RAG with Israeli content:** Connect a Vector Store node (Pinecone, Qdrant, Supabase pgvector) to an AI Agent. Use a multilingual embedding model that handles Hebrew (Cohere `embed-multilingual-v3.0` or OpenAI `text-embedding-3-large`); `text-embedding-ada-002` is weak on Hebrew.
**Example: AI bank transaction categorizer.** Schedule -> Code (bank scraper) -> AI Agent (categorize) -> Google Sheets:
```javascript
return $input.all().map(item => ({ json: {
date: item.json.date, description: item.json.description, amount: item.json.chargedAmount,
prompt: `Categorize this Israeli bank transaction. Transaction: "${item.json.description}" for ${item.json.chargedAmount} NIS on ${item.json.date}.
Categories: הכנסות, שכר, ספקים, מע"מ, ביטוח לאומי, שכירות, הוצאות משרד, אחר.
Respond with ONLY the Hebrew category name.`
}}));
```
## n8n MCP nodes
- **MCP Client Tool** (`@n8n/n8n-nodes-langchain.mcpClientTool`): attach as a sub-node so an AI Agent can call tools on an external MCP server (e.g. agentskills.co.il's `hebcal`, `israeli-bank`, `data-gov-il` servers).
- **MCP Server Trigger** (`@n8n/n8n-nodes-langchain.mcpTrigger`): exposes an n8n workflow itself as an MCP tool, so external clients (Claude Desktop, Cursor, Windsurf, custom GPTs) can discover and invoke your Morning-invoice-lookup or bank-scraper workflow. From typeVersion 2 it also offers n8n User Auth (OAuth2).
There is no `toolMcp` node. That name is a common hallucination and n8n rejects a workflow JSON that uses it.
---
# Troubleshooting (full detail, moved from SKILL.md)
## Troubleshooting detail
### Morning (Green Invoice) API returns 401 Unauthorized
JWT expired (60 min TTL). Add a token refresh step at the start of every execution. Store the token in `$getWorkflowStaticData('global')` with a timestamp and refresh if older than 55 min.
### Hebrew text appears garbled in CSV export
Missing UTF-8 BOM, so Excel reads it as ANSI. Prepend `''` to CSV content, or set Spreadsheet File encoding to UTF-8-BOM.
### Webhook not receiving Cardcom callbacks
Cardcom needs the callback URL publicly accessible with valid SSL. Use nginx/Caddy + Let's Encrypt. Ensure `WEBHOOK_URL` matches the public URL. Whitelist n8n's IP in the Cardcom dashboard.
### Schedule Trigger runs during Shabbat despite Hebcal check
Server timezone is UTC, not Asia/Jerusalem. Verify `GENERIC_TIMEZONE=Asia/Jerusalem`, restart n8n, and log `new Date().toString()` in a Code node to confirm.
### israeli-bank-scrapers fails in Code node
Three separate causes, in the order they bite:
1. `require()` is blocked. Set `NODE_FUNCTION_ALLOW_EXTERNAL=israeli-bank-scrapers` **on the task runner** (n8n 2.0 enables runners by default, and the variable has no effect on the main container in that setup).
2. Credentials come back undefined. `$env` is blocked inside Code nodes in 2.x (`N8N_BLOCK_ENV_ACCESS_IN_NODE=true`). Pull them from the credential store instead.
3. Wrong credential keys. The key is `password`, not `userPassword`, and the first field is per-bank (`userCode` for Hapoalim). See the login-fields table in Step 2.
Also install the package and its Puppeteer/Playwright dependency in the runner image, and give the container >= 1GB memory for Chromium. Execute Command (legacy approach) is disabled by default in 2.0.
### Cloudflare blocks bank scraper for Amex/Isracard
Switch to the maintained fork: `npm install @sergienko4/israeli-bank-scrapers` (uses Camoufox).
---
# בחירת פלטפורמה, מבנה JSON ו-credentials (הועבר מ-SKILL.md שלבים 8-10)
## שלב 8: מתי להשתמש ב-n8n לעומת חלופות
| קריטריון | n8n | Make.com | Zapier |
|----------|-----|----------|--------|
| אירוח עצמי (מיקום נתונים) | כן (Docker, כל ענן) | לא (SaaS בלבד) | לא (SaaS בלבד) |
| צמתי API ישראליים | אין מובנים, HTTP/Code | קצת מהקהילה | מעט מאוד |
| מגבלת תהליכים | ללא הגבלה (אירוח עצמי) | לפי תוכנית | לפי תוכנית |
| הרצת קוד | Code nodes מלאים (JS/Python) | JS מוגבל | מוגבל |
| צמתי AI Agent | 70+ צמתי AI, תמיכה ב-MCP | יכולות AI | יכולות AI |
| מחיר (אירוח עצמי) | חינם (קוד פתוח) | לא רלוונטי | לא רלוונטי |
| ממשק בעברית | לא (אנגלית בלבד) | חלקי | לא |
| מתאים ל | מפתחים שצריכים שליטה מלאה, מיקום נתונים, אוטומציות ללא הגבלה | משתמשים לא טכניים שרוצים בונה ויזואלי | אינטגרציות פשוטות, משתמשים לא טכניים |
בחרו n8n כש: צריכים אירוח עצמי למיקום נתוני ישראל, אוטומציות ללא הגבלה, גישה מלאה לקוד לטיפול ב-API ישראליים (קידוד עברית, פורמט טלפונים, חישובי מע"מ), או יכולות AI Agent עם הקשר ישראלי.
## שלב 9: ייבוא וייצוא של workflow כ-JSON
תהליכי n8n הם מסמכי JSON. סוכנים שבונים תהליכים בצורה פרוגרמטית (במקום ללחוץ בממשק) חייבים להבין את המבנה:
```json
{
"name": "Morning daily reconciliation",
"nodes": [
{
"parameters": { "rule": { "interval": [{ "field": "cronExpression", "expression": "0 6 * * 0-4" }] } },
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [240, 300]
}
],
"connections": {
"Schedule Trigger": { "main": [[{ "node": "Get Token", "type": "main", "index": 0 }]] }
}
}
```
מבנה עיקרי:
- **`nodes`**: מערך של אובייקטי צמתים. לכל אחד `name` (ייחודי, משמש כמפתח חיבור), `type` (למשל `n8n-nodes-base.httpRequest`), `typeVersion` (חייב להתאים לגרסה ש-n8n תומך בה, אחרת הייבוא נכשל), `parameters` (הגדרות הצומת) ו-`position` (קואורדינטות `[x, y]`).
- **`connections`**: אובייקט שממופתח לפי `name` של צומת המקור, וממפה פלט (`main`) למערך של מערכים של יעדים `{ node, type, index }`. המערך הכפול מאפשר פלטים מרובים (למשל ענפי צומת IF).
- ייצוא דרך הממשק ("Download") או `GET /api/v1/workflows/{id}`; ייבוא דרך "Import from File" או `POST /api/v1/workflows`. אחרי ייבוא ל-n8n 2.0 חובה לפרסם את התהליך לפני שהוא רץ. ערכי `typeVersion` משתנים בין גרסאות, לכן בנו JSON מול גרסת n8n ידועה.
## שלב 10: הגדרת credentials ל-API ישראליים
n8n שומר סודות ב-credential store מוצפן, לעולם לא בתוך ה-workflow JSON:
- **JWT של Morning (חשבונית ירוקה)**: אין credential מובנה. משרשרים HTTP Request nodes, הראשון קורא ל-`/account/token` עם ה-API key וה-secret, הצמתים הבאים שולחים `Authorization: Bearer {{token}}` דרך **Header Auth** או ביטוי. הטוקן פג אחרי 60 דקות, אז מרעננים בכל הרצה במקום לשמור אותו לטווח ארוך.
- **שערי SMS ישראליים (019, InforUMobile)**: יוצרים credential מסוג **Header Auth**, שם `Authorization`, ערך `Bearer <token>`, ומצרפים ל-HTTP Request node.
- **שערי תשלום (Cardcom, Tranzila, Grow)**: שומרים מזהי סוחר / מפתחות API כערכי **Generic Credential** שמופנים דרך `{{$credentials.fieldName}}`. בקשות ה-`multipart/form-data` של Grow עדיין שולפות סודות מה-credential, לא מגוף הצומת.
- באירוח עצמי, הגדירו `N8N_ENCRYPTION_KEY` יציב כדי שה-credential store יישאר ניתן לפענוח בין הפעלות מחדש.
---
# צמתי AI Agent לתהליכים ישראליים (הועבר מ-SKILL.md שלב 7)
## שלב 7: צמתי AI Agent של n8n לתהליכים ישראליים
n8n 2.x מגיע עם אינטגרציית LangChain מובנית (קבוצת הצמתים "Advanced AI"). מעל 70 צמתי AI: Tools Agent, Conversational Agent, צמתי זיכרון (Window Buffer, Summary Buffer), צמתי Vector Store ל-RAG (Pinecone, Qdrant, Supabase pgvector), וצמתי Model עבור OpenAI, Anthropic ומודלים מקומיים דרך Ollama.
**אל תקבעו שם מודל קשיח מתוך מדריך כלשהו.** צומת Anthropic Chat Model הנוכחי מושך את רשימת המודלים שלו מה-API של Anthropic בזמן ריצה ולא מרשימה נעולה, ולכן הרשימה זזה בלי גרסה חדשה של n8n. בחרו לפי הדרישה, וקחו את מה שהצומת מציע היום:
**בחירת מודל לתוכן ישראלי:**
| שימוש | מודל מומלץ | למה |
|-------|-----------|------|
| סיווג תנועות בעברית | מודל frontier מדרג הביניים (ברירת המחדל של הצומת היא ה-Sonnet החדש ביותר) | עברית טובה בעלות נמוכה; המשימה היא סיווג ולא הסקה |
| סיכום מסמכים עברית (PDF ארוכים) | המודל החזק ביותר הזמין | טקסט משפטי עברי ארוך מרוויח מעומק הסקה |
| צ'אט עברית בזמן אמת | המודל בעל הלטנסי הנמוך ביותר באותה משפחה | תורות עברית קצרות תחומות בלטנסי ולא באיכות |
| On-prem / מיקום נתונים בארץ | Ollama (Llama 3.1, Qwen 2.5) על VPS ישראלי | שומר PII בארץ; העברית של Llama 3.1 סבירה לסיווג, חלשה ליצירה |
**RAG על תוכן ישראלי (צמתי Vector Store):** מחברים צומת Vector Store (Pinecone, Qdrant או Supabase pgvector) ל-AI Agent כדי לבצע retrieval על קורפוסים בעברית (היסטוריית חשבוניות, PDF של חוקי מס, יומני צ'אט לקוחות). השתמשו במודל embedding רב-לשוני שמטפל בעברית (Cohere `embed-multilingual-v3.0` או OpenAI `text-embedding-3-large`); ברירת המחדל `text-embedding-ada-002` חלשה בעברית לעומת שפות בכתב לטיני.
**דוגמה: סיווג אוטומטי של תנועות בנק עם AI**
ארכיטקטורה: Schedule Trigger -> Code (סריקת בנק) -> AI Agent (סיווג) -> Google Sheets
```javascript
// Code node: הכנת תנועות לסיווג AI
const transactions = $input.all().map(item => ({
json: {
date: item.json.date,
description: item.json.description,
amount: item.json.chargedAmount,
prompt: `סווג את תנועת הבנק הישראלית הזו למטרות הנהלת חשבונות.
תנועה: "${item.json.description}" על סך ${item.json.chargedAmount} ש"ח בתאריך ${item.json.date}.
קטגוריות: הכנסות, שכר, ספקים, מע"מ, ביטוח לאומי, שכירות, הוצאות משרד, אחר.
השב עם שם הקטגוריה בלבד.`
}
}));
return transactions;
```
מחברים את הפלט של ה-Code node ל-AI Agent node (Tools Agent) שמוגדר עם ה-LLM המועדף. הסוכן מסווג כל תנועה לפי התיאור העברי וקטגוריות ההוצאות הישראליות המוכרות.
**אינטגרציית MCP ב-n8n (שני צמתים):** n8n 2.x מגיע עם שני צמתי MCP מובנים:
- **MCP Client Tool** (`@n8n/n8n-nodes-langchain.mcpClientTool`): מתחבר כצומת משנה ל-AI Agent כך שהסוכן יקרא לכלים שחשופים בשרת MCP חיצוני. שימושי לחיבור שרתי MCP מ-agentskills.co.il כמו `hebcal`, `israeli-bank` או `data-gov-il` לסוכנים שלכם.
- **MCP Server Trigger**: חושף תהליך n8n כשלעצמו ככלי MCP. לקוחות AI חיצוניים (Claude Desktop, Cursor, Windsurf, GPT מותאמים) יכולים לגלות ולהפעיל את התהליך כאילו הוא כלי native. שימושי לעטיפת תהליך חיפוש חשבוניות Morning או סורק בנק כך שכל עוזר AI במשרד יוכל להפעיל אותו לפי דרישה.
ביחד הצמתים האלה הופכים את n8n גם למארח כלים וגם לצרכן כלים בסטאק סוכני מבוסס MCP.
---
# פתרון בעיות (פירוט מלא, הועבר מ-SKILL.md)
## פתרון בעיות
## שגיאה: "Morning API מחזיר 401 Unauthorized"
סיבה: ה-JWT token פג תוקף. לטוקנים של Morning יש TTL של 60 דקות.
פתרון: הוספת שלב רענון טוקן בתחילת כל הרצת תהליך. שמירת הטוקן ב-static data של n8n (`$getWorkflowStaticData('global')`) עם חותמת זמן, ורענון אם עבר יותר מ-55 דקות.
## שגיאה: "טקסט עברי מופיע משובש בייצוא CSV"
סיבה: ה-CSV חסר BOM (Byte Order Mark) של UTF-8, אז Excel מפרש אותו כ-ANSI.
פתרון: ב-Code node שמכין נתוני CSV, מוסיפים BOM בתחילה: `'\uFEFF' + csvContent`. לחלופין, מגדירים את אפשרות ה-encoding של Spreadsheet File node ל-UTF-8-BOM.
## שגיאה: "Webhook לא מקבל callbacks מ-Cardcom"
סיבה: Cardcom דורש שה-callback URL יהיה נגיש מהאינטרנט עם תעודת SSL תקינה. n8n באירוח עצמי מאחורי firewall לא יקבל callbacks.
פתרון: שימוש ב-reverse proxy (nginx, Caddy) עם SSL של Let's Encrypt. וידוא שמשתנה הסביבה `WEBHOOK_URL` תואם ל-URL הציבורי. הוספת ה-IP של n8n לרשימה המורשית בלוח הבקרה של Cardcom.
## שגיאה: "Schedule Trigger רץ בשבת למרות בדיקת Hebcal"
סיבה: אזור הזמן של שרת n8n מוגדר ל-UTC במקום Asia/Jerusalem, כך שהשוואת זמני שבת מוסטת ב-2-3 שעות.
פתרון: וידוא `GENERIC_TIMEZONE=Asia/Jerusalem` במשתני הסביבה של n8n. הפעלה מחדש של n8n אחרי שינוי הגדרות אזור זמן. בדיקה על ידי הדפסת `new Date().toString()` ב-Code node.
## שגיאה: "israeli-bank-scrapers נכשל ב-Code node"
סיבה: ב-n8n 2.0, Code nodes רצים ב-task runner מבודד. חבילת `israeli-bank-scrapers` והתלויות שלה (Puppeteer/Playwright) עשויות לא להיות זמינות ב-sandbox.
פתרון: התקנת `israeli-bank-scrapers` כחבילת npm שנגישה ל-task runner של n8n. וידוא שה-Docker container של n8n מקצה מספיק זיכרון (לפחות 1GB) ל-Chromium.
## שגיאה: "Cloudflare חוסם סורק בנקים עבור אמקס/ישראכרט"
סיבה: מתחילת 2026, Cloudflare חוסם דפדפנים headless באתרים פיננסיים ישראליים מסוימים.
פתרון: מעבר לפורק המתוחזק `@sergienko4/israeli-bank-scrapers` שמשתמש ב-Camoufox לעקיפת חסימת Cloudflare. התקנה: `npm install @sergienko4/israeli-bank-scrapers`.
references/shabbat-cron-patterns.md
# Shabbat-Aware Scheduling Patterns for n8n
Pre-built patterns for n8n workflows that need to respect Shabbat and Jewish holidays.
## Core Concept
n8n's built-in Schedule Trigger node has no concept of Shabbat or Jewish holidays. The solution is a two-node pattern at the start of every scheduled workflow:
1. **Schedule Trigger** fires on schedule
2. **Shabbat Gate** (HTTP Request + Code) checks if it is currently Shabbat or a holiday, and stops the workflow if so
This pattern is appended to the beginning of every schedule-triggered workflow. It adds ~500ms latency per check (one API call to Hebcal).
## Pattern 1: Weekly Business Workflow (Sunday-Thursday)
For workflows that should run during Israeli business days only.
**Schedule Trigger cron expression:** `0 9 * * 0-4` (9:00 AM, Sunday through Thursday)
Israeli business week is Sunday (0) through Thursday (4), so this cron never fires on Shabbat itself.
**That is not the same as "no gate needed", and treating it that way is how a workflow runs on Yom Kippur.** Six of Israel's eight yom tov days in 2026 fall inside Sunday-to-Thursday: Pesach I (Thu 2 Apr), Pesach VII (Wed 8 Apr), Rosh Hashana II (Sun 13 Sep), Yom Kippur (Mon 21 Sep), and the surrounding erev days. A weekday cron therefore still needs the holiday gate from Pattern 3. Excluding Friday and Saturday only removes Shabbat, and Shabbat is the minority of the problem.
Note also that the cron is interpreted in the instance timezone. With `GENERIC_TIMEZONE=Asia/Jerusalem` set as the skill requires, `0 9` means 09:00 Israel time. Do not carry over UTC-based cron expressions from a tutorial.
## Pattern 2: Daily Workflow with Shabbat Gate
For workflows that run every day but must pause during Shabbat.
**Schedule Trigger cron expression:** `0 */3 * * *` (every 3 hours)
**Shabbat Gate Code Node:**
**Two bugs to avoid, both of which let the workflow run when it must not.**
*Bug 1: taking the first `candles` and the first `havdalah` and assuming they bracket a period.* They often do not. When the request is made DURING a chag, Hebcal returns that chag's `havdalah` first and the NEXT festival's `candles` days later, so the "interval" runs backwards and `now >= start && now <= end` can never be true. Verified against the live API: on Yom Kippur, `/shabbat?geonameid=293397&gy=2026&gm=9&gd=21` returns `havdalah 2026-09-21T19:15+03:00` BEFORE `candles 2026-09-25T18:13+03:00`. A naive gate lets the workflow run on Yom Kippur. Pair each `candles` with the FIRST `havdalah` that comes after it, and reject any pair that is not ordered.
*Bug 2: failing open.* If Hebcal is unreachable, WAF-blocked, or returns a changed shape, `items` is missing and a `proceed with caution` fallback sends customer messages during Shabbat. A gate whose failure mode is "run anyway" is not a gate. **Fail closed**, and use a cached value if you have one.
```javascript
// Runs after the HTTP Request to the Hebcal /shabbat endpoint.
const now = new Date();
const items = $input.first().json?.items;
// FAIL CLOSED: no usable calendar data means we do not know, so we do not run.
if (!Array.isArray(items) || items.length === 0) {
return []; // optionally: fall back to a cached window before giving up
}
// Pair each candle-lighting with the first havdalah AFTER it.
const events = items
.filter(i => i.category === 'candles' || i.category === 'havdalah')
.map(i => ({ cat: i.category, at: new Date(i.date) }))
.sort((a, b) => a.at - b.at);
let inRest = false;
for (let k = 0; k < events.length - 1; k++) {
if (events[k].cat === 'candles' && events[k + 1].cat === 'havdalah') {
if (now >= events[k].at && now <= events[k + 1].at) { inRest = true; break; }
}
}
// A havdalah with no preceding candles in the window means the rest period
// started before the data we were given. Treat "now is before the first
// havdalah" as still resting.
if (!inRest && events.length && events[0].cat === 'havdalah' && now <= events[0].at) {
inRest = true;
}
return inRest ? [] : $input.all();
```
This covers Shabbat and any yom tov that Hebcal brackets with candles/havdalah. It does not by itself cover a chag day that the `/shabbat` window does not reach; for that add Pattern 3.
**Hebcal HTTP Request node configuration:**
```
Method: GET
URL: https://www.hebcal.com/shabbat
Query Parameters:
cfg: json
geonameid: 293397 (Tel Aviv, change per your location)
M: on
```
## Pattern 3: Holiday-Aware Scheduling
For workflows that must also pause on Jewish holidays (Yom Tov).
**Extended Code Node (replaces the basic Shabbat gate):**
Two further traps live in this pattern specifically.
*The date must be computed in Israel time, not UTC.* `now.toISOString().split('T')[0]` is a UTC date, so between midnight and 03:00 Israel time it yields YESTERDAY, and the gate reads the wrong day on every chag night. Use `Intl.DateTimeFormat` with `timeZone: 'Asia/Jerusalem'`.
*A yom tov date-equality test cannot express erev chag.* Hebcal returns yomtov entries as bare dates (`"2026-09-21"`) with no start time, but the chag actually begins at candle lighting the previous evening. Matching only on today's date leaves the entire erev-chag evening unprotected. Treat the evening of the preceding day as blocked once candle-lighting time has passed, which the corrected Pattern 2 gate handles when the `/shabbat` window reaches the chag.
```javascript
const now = new Date();
const shabbatItems = $('Shabbat Check').first().json?.items;
const holidayItems = $('Holiday Check').first().json?.items;
// FAIL CLOSED on either source.
if (!Array.isArray(shabbatItems) || !Array.isArray(holidayItems)) return [];
// 1. Candle-lighting / havdalah window: the paired scan from Pattern 2.
const ev = shabbatItems
.filter(i => i.category === 'candles' || i.category === 'havdalah')
.map(i => ({ cat: i.category, at: new Date(i.date) }))
.sort((a, b) => a.at - b.at);
for (let k = 0; k < ev.length - 1; k++) {
if (ev[k].cat === 'candles' && ev[k + 1].cat === 'havdalah'
&& now >= ev[k].at && now <= ev[k + 1].at) return [];
}
if (ev.length && ev[0].cat === 'havdalah' && now <= ev[0].at) return [];
// 2. Yom tov by Israel-local date, plus the evening before.
const ilDate = d => new Intl.DateTimeFormat('en-CA', {
timeZone: 'Asia/Jerusalem', year: 'numeric', month: '2-digit', day: '2-digit',
}).format(d);
const today = ilDate(now);
const tomorrow = ilDate(new Date(now.getTime() + 86400000));
// hourCycle 'h23' matters: with the default some engines render midnight as
// '24', and Number('24') >= 16 would over-block the 00:00 hour.
const ilHour = Number(new Intl.DateTimeFormat('en-GB', {
timeZone: 'Asia/Jerusalem', hour: '2-digit', hourCycle: 'h23',
}).format(now));
const isYomTov = holidayItems.some(i => i.yomtov === true && i.date.startsWith(today));
// Erev chag. 16:00 is a deliberately conservative cutoff, not a halachic time:
// the earliest Israeli candle lighting is around 16:00 in December. Prefer the
// exact candle-lighting time from the /shabbat response whenever it covers the
// chag, and fall back to this only when it does not.
const isErevYomTov = ilHour >= 16 &&
holidayItems.some(i => i.yomtov === true && i.date.startsWith(tomorrow));
if (isYomTov || isErevYomTov) return [];
return $input.all();
```
**Do not silently DROP the work.** `return []` ends the branch, so an invoice send or a customer notification that arrives during Shabbat is lost, not delayed. For anything a customer is waiting on, write the items to `$getWorkflowStaticData('global')` or a database instead, and drain that queue from a separate post-havdalah workflow. "It was Shabbat so the customer never got their invoice" is a data-loss bug wearing a compliance costume.
**Beyond yom tov.** Hebcal also returns fast days (Tisha B'Av, Tzom Gedaliah, with `Fast begins` / `Fast ends` zmanim items), Yom HaZikaron and Yom HaAtzmaut, and chol hamoed. None of these are `yomtov: true`, so the gate above ignores them, which is usually correct for chol hamoed and usually wrong for Tisha B'Av and the national days. Decide per workflow rather than inheriting the default.
**Holiday HTTP Request node configuration:**
```
Method: GET
URL: https://www.hebcal.com/hebcal
Query Parameters:
v: 1
cfg: json
year: now
month: x
maj: on
mod: on
i: on
```
Two parameters are load-bearing and both fail silently if you get them wrong:
- **`month: x`, not `month: now`.** `now` is not a valid value for this parameter. Hebcal returns HTTP 200 with `"items": []`, the `.some(...)` check below evaluates false, and the workflow runs on Yom Kippur with no error raised anywhere. `x` returns the whole year; a numeric month also works.
- **`i: on` selects the Israel schedule.** Without it Hebcal serves the Diaspora calendar (`"title": "Hebcal Diaspora 2026"`), which reports 13 yomtov days for 2026 against Israel's 8. Passing an Israeli `geonameid` has the same effect.
Verify with the response `title`: it must read `Hebcal Israel <year>` or `Hebcal <city> <year>`, never `Hebcal Diaspora <year>`.
**Workflow structure:**
```
Schedule Trigger -> [Shabbat Check HTTP] -> [Holiday Check HTTP] -> [Gate Code] -> rest of workflow
(parallel) (parallel)
```
Optimization: Run both HTTP requests in parallel using n8n's split/merge pattern, then feed both results into the Gate Code node.
## Pattern 4: Friday Early Cutoff
For workflows that should stop before Shabbat on Friday (e.g., stop processing orders 2 hours before candle lighting).
```javascript
const now = new Date();
const items = $input.first().json?.items;
// FAIL CLOSED: without the calendar we cannot know how close we are to candle lighting.
if (!Array.isArray(items)) return [];
// The NEXT candle lighting, not merely the first item in the array. On a chag
// the first candles entry can be days away while a rest period is already open,
// so run the Pattern 2 gate first and use this only as the pre-cutoff.
const next = items
.filter(i => i.category === 'candles')
.map(i => new Date(i.date))
.filter(d => d > now)
.sort((a, b) => a - b)[0];
if (!next) return []; // no upcoming candle lighting in the window: fail closed
const cutoff = new Date(next.getTime() - 2 * 60 * 60 * 1000);
return now >= cutoff ? [] : $input.all();
```
Use case: E-commerce order processing that should not start new fulfillment workflows close to Shabbat, because they cannot be completed before candle lighting.
## Pattern 5: Post-Shabbat Resume
For workflows that should run as soon as Shabbat ends (e.g., send queued notifications after havdalah).
**Schedule Trigger cron expression:** `*/15 17-20 * * 6` (every 15 minutes, 5-8 PM on Saturday)
```javascript
const now = new Date();
const data = $input.first().json;
// The MOST RECENT havdalah that has already passed, not the first in the array:
// on a chag the array can open with a havdalah days ahead of now.
const shabbatEnd = (data.items ?? [])
.filter(i => i.category === 'havdalah')
.map(i => new Date(i.date))
.filter(d => d <= now)
.sort((a, b) => b - a)[0];
if (shabbatEnd) {
// Only proceed if we are within 30 minutes after havdalah
const window = new Date(shabbatEnd.getTime() + 30 * 60 * 1000);
if (now >= shabbatEnd && now <= window) {
return $input.all(); // Shabbat just ended, process queued items
}
}
return []; // Not the right time
```
## Pattern 6: Monthly with Holiday Offset
For workflows that run on a specific day each month but shift when that day falls on Shabbat or a holiday.
Two things this pattern must NOT do, both of which an earlier version of it did: use the naive
`items.find('candles')` / `items.find('havdalah')` pair (broken on a chag, see Bug 1 above), and
compute the day-of-month from the server clock rather than Israel time.
```javascript
const now = new Date();
const targetDay = 1; // 1st of each month
const items = $input.first().json?.items;
// FAIL CLOSED: no calendar data means we do not know whether it is Shabbat.
if (!Array.isArray(items) || items.length === 0) return [];
// Day of month in Israel time, not server time.
const ilParts = new Intl.DateTimeFormat('en-CA', {
timeZone: 'Asia/Jerusalem', year: 'numeric', month: '2-digit', day: '2-digit',
}).format(now).split('-');
const currentDay = Number(ilParts[2]);
// Same paired forward scan as Pattern 2. Do not shortcut it.
const ev = items
.filter(i => i.category === 'candles' || i.category === 'havdalah')
.map(i => ({ cat: i.category, at: new Date(i.date) }))
.sort((a, b) => a.at - b.at);
let resting = false;
for (let k = 0; k < ev.length - 1; k++) {
if (ev[k].cat === 'candles' && ev[k + 1].cat === 'havdalah'
&& now >= ev[k].at && now <= ev[k + 1].at) { resting = true; break; }
}
if (!resting && ev.length && ev[0].cat === 'havdalah' && now <= ev[0].at) resting = true;
if (resting) return []; // never run during the rest period
// Run on the target day, or on the first following day once the rest period has passed.
if (currentDay >= targetDay && currentDay <= targetDay + 2) return $input.all();
return [];
```
The postponement window here is deliberately crude (up to two days). If the exact "first working day
on or after the 1st" matters, persist a `lastRunMonth` marker in `$getWorkflowStaticData('global')`
and check it rather than inferring from the date, otherwise a three-day chag block will skip the
month entirely.
## Caching Shabbat Data
To avoid calling Hebcal on every schedule trigger tick, cache the weekly Shabbat times:
```javascript
const staticData = $getWorkflowStaticData('global');
const now = Date.now();
const ONE_DAY = 24 * 60 * 60 * 1000;
if (staticData.shabbatData && staticData.fetchedAt > now - ONE_DAY) {
// Use cached data
return [{ json: staticData.shabbatData }];
}
// Fetch fresh data (pass to next HTTP Request node)
return $input.all();
```
After the HTTP Request, store the result:
```javascript
const staticData = $getWorkflowStaticData('global');
staticData.shabbatData = $input.first().json;
staticData.fetchedAt = Date.now();
return $input.all();
```
## Major Jewish Holidays Reference
Holidays where `yomtov: true` on the **Israel** schedule (work restrictions apply, treat like Shabbat):
| Holiday | Hebrew | Typical Month | Yom Tov days |
|---------|--------|---------------|--------------|
| Pesach | פסח | March-April | 2 (Pesach I and Pesach VII) |
| Shavuot | שבועות | May-June | 1 |
| Rosh Hashana | ראש השנה | September-October | 2 |
| Yom Kippur | יום כיפור | September-October | 1 |
| Sukkot | סוכות | September-October | 1 (Sukkot I) |
| Shmini Atzeret | שמיני עצרת | September-October | 1 |
Eight yomtov days per year. In Israel, Simchat Torah falls on Shmini Atzeret (one day, one row), so do not count it separately; the second days of Pesach, Shavuot and Sukkot are Diaspora-only and are ordinary working days here. If your query returns Pesach II, Pesach VIII, Shavuot II, Sukkot II or a standalone Simchat Torah, you are reading the Diaspora calendar and are missing `i=on`.
## Common Mistakes
1. **Using fixed Shabbat times.** Shabbat timing varies by 1+ hour throughout the year in Israel (earliest candle lighting ~4:00 PM in December, latest ~7:45 PM in June). Always use the Hebcal API for current times.
2. **Forgetting Erev holidays.** Some holidays start at sundown the day before (like Shabbat). If your workflow runs Friday afternoon, it needs to check both Shabbat and any holiday that starts Friday night.
3. **Not handling DST transitions.** Israel switches to summer time (IDT, UTC+3) on the Friday before the last Sunday of March, and back to winter time (IST, UTC+2) on the last Sunday of October. A schedule trigger at "9 AM" will fire at a different UTC time after the transition. Ensure `GENERIC_TIMEZONE=Asia/Jerusalem` is set so n8n handles this automatically.
4. **Hardcoding a single city.** If your business serves customers across Israel, candle lighting times can differ by 10+ minutes between cities. Jerusalem is especially different due to the tradition of lighting 40 minutes before sunset (vs 18 minutes in most cities, 30 minutes in Haifa and Zikhron Ya'akov). Choose the earliest candle lighting time among your relevant cities for the safest cutoff.
5. **Ignoring Chol HaMoed.** The intermediate days of Sukkot and Pesach (Chol HaMoed) are not full Yom Tov, but many Israeli businesses operate on reduced hours. If your workflow involves customer-facing operations, consider pausing or reducing frequency during Chol HaMoed as well.
references/webhook-auth-patterns.md
# Webhook Authentication Patterns for n8n
This reference covers HMAC signature verification and JWT claim validation patterns for n8n Webhook nodes processing Israeli payment-gateway and form-submission callbacks. See `SKILL.md` Step 5 for the higher-level decision table on auth modes.
## Auth mode picker
| Mode | Where it lives | When to use it |
|------|---------------|----------------|
| None | Webhook node "Authentication" dropdown | Local testing only; never in production |
| Basic Auth | Generic Credential | Internal/private webhooks behind a VPN |
| Header Auth | Header Auth credential (e.g. `X-API-Key: <token>`) | Default for Israeli SMS callbacks and internal webhooks |
| JWT Auth | JWT credential (HMAC HS256/384/512 or RSA/ECDSA via PEM) | Cross-org integrations where the caller already issues JWTs |
Given the 2026 unauthenticated-webhook CVE history (CVE-2026-21858 file access on 1.x, CVE-2026-27493 expression injection on 1.x and early 2.x), the "None" mode on a publicly-routable webhook is effectively a vulnerability. Pick one of the other three for every payment-gateway flow.
## First: the Israeli payment gateways do NOT send an HMAC
Read this before reaching for a signature check. Cardcom's v11 result object carries **no signature field** of any kind (the spec exposes only `WebHookUrl`), and Grow ships a shared `webhookKey` **inside the body**. Neither is an HMAC. Writing an HMAC verifier for them protects nothing, and a public webhook URL with a static header is forgeable by anyone who learns the URL: a forged POST carrying a plausible `paymentSum` / `asmachta` will make your workflow issue a real tax invoice and burn a real allocation number.
**The verification that actually works for these gateways is a server-side re-read.** Never trust the callback body as the source of truth:
| Gateway | What arrives | What to verify against |
|---|---|---|
| Cardcom | `LowProfileResult` on your `WebHookUrl` | Re-read the transaction server-side by `LowProfileId` / `TranzactionId` and confirm `ResponseCode == 0` and the amount, before issuing any document |
| Grow by Meshulam | `multipart/form-data` POST with `webhookKey`, `asmachta`, `paymentSum` | Compare `webhookKey` against your stored key (constant-time), then re-read via `getPaymentProcessInfo`, then call `approveTransaction` to finalize |
| Tranzila | GET params incl. `Response`, `index`, `sum` | Re-read the transaction by `index`; `Response=000` alone is not proof of origin |
Do all of this **before** creating the invoice, and deduplicate on the gateway's own transaction id so a retried webhook cannot issue a second document.
## HMAC signature verification (for senders that actually sign)
Use this only where the sender genuinely signs its payload, which among the gateways here means custom integrations rather than Cardcom or Grow. n8n has no built-in HMAC verifier, so it goes in a Code node directly after the Webhook. Three things break the naive version:
1. **The item shape.** The Webhook node emits `{ json: { headers, params, query, body } }`. Reading `$input.first().headers` gets `undefined`.
2. **The secret.** `$env` is blocked inside Code nodes by default in n8n 2.x, so `$env.WEBHOOK_HMAC_SECRET` silently yields `undefined` and the comparison runs against a garbage key. Set `N8N_BLOCK_ENV_ACCESS_IN_NODE=false` **on the task runner**, or pass the secret in from a preceding credential-bearing node (accepting that it then appears in execution data).
3. **The raw body.** `JSON.stringify(body)` is NOT what the sender signed. Key order, whitespace and unicode escaping all differ, so the HMAC mismatches even when everything else is right. You need the raw bytes; if your n8n version does not expose them, capture them at the reverse proxy.
```javascript
// Requires NODE_FUNCTION_ALLOW_BUILTIN=crypto on the task runner.
const crypto = require('crypto');
const item = $input.first().json; // NOT $input.first()
const signature = item.headers['x-signature'];
const raw = item.rawBody ?? JSON.stringify(item.body); // see caveat 3 above
const secret = $env.WEBHOOK_HMAC_SECRET; // see caveat 2 above
if (!secret) throw new Error('HMAC secret unavailable, refusing to accept the webhook');
const expected = crypto.createHmac('sha256', secret).update(raw).digest('hex');
const a = Buffer.from(signature || '', 'hex');
const b = Buffer.from(expected, 'hex');
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
throw new Error('Invalid HMAC signature');
}
return $input.all();
```
Use `crypto.timingSafeEqual`, never `===`. Both Buffers must be the same length or it throws, which the explicit length check handles. Fail closed: if the secret is missing, reject the webhook rather than accepting it.
## JWT claim validation (what n8n does and does not check)
**Expiry IS enforced.** n8n's Webhook JWT Auth calls `jwt.verify(token, secretOrPublicKey, { algorithms: [...] })` from `jsonwebtoken`, which rejects an expired token by default. An earlier version of this reference claimed otherwise; that was wrong. What n8n does NOT check is `iss` and `aud`, because it passes no `issuer` / `audience` options.
So add a claim check only for issuer and audience, and only **after** n8n's own signature verification has run (auth mode JWT Auth). Do not use this as a substitute for signature verification: the snippet below decodes rather than verifies, so on a webhook whose auth mode is None or Header Auth an attacker can mint any payload they like and pass it.
```javascript
// Only valid downstream of the Webhook node's JWT Auth mode, which has
// already verified the signature and the exp claim.
const item = $input.first().json;
const token = item.headers.authorization?.replace(/^Bearer\s+/i, '');
if (!token) throw new Error('Missing token');
const [, payloadB64] = token.split('.');
const payload = JSON.parse(Buffer.from(payloadB64, 'base64url').toString());
if (payload.iss !== EXPECTED_ISSUER) throw new Error('Bad iss');
// aud may legitimately be an array (RFC 7519), so do not compare with !==
const aud = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
if (!aud.includes(EXPECTED_AUDIENCE)) throw new Error('Bad aud');
return $input.all();
```
`EXPECTED_ISSUER` and `EXPECTED_AUDIENCE` are shown as bare identifiers deliberately: `$env` reads are blocked in Code nodes by default in 2.x, so supply them from a preceding node or unblock env access on the runner. Do not paste `$env.X` here and assume it resolved.
## IP whitelisting
Cardcom and Tranzila require your webhook server's IP to be whitelisted in their dashboards. If self-hosting n8n, use a static IP or configure a reverse proxy with a fixed egress IP.
scripts/.gitkeep
SKILL_HE.md
---
name: n8n-hebrew-workflows
description: >-
Build and optimize n8n 2.x automation workflows (stable line 2.38 as of September 2026) with
Israeli API integrations including Morning (formerly Green Invoice), EZCount,
israeli-bank-scrapers, data.gov.il, Israeli SMS gateways, and payment processors
(Cardcom v11, Tranzila API v2, Grow by Meshulam). Covers the n8n CVE-2026-44789 critical
patch line, AI Agent nodes with native LangChain + RAG, MCP Client Tool
and MCP Server Trigger nodes, Israel Invoice Reform 2026 (5,000 NIS pre-VAT threshold
from June 2026), Hebrew data handling in Code nodes, and Israel-schedule Shabbat and
holiday gating.
license: MIT
---
# תהליכי עבודה n8n בעברית
## הנחיות
### שלב 1: זיהוי תבנית האוטומציה
לפני שבונים משהו, מתאימים את הצורך העסקי לתבנית n8n מתאימה:
| צורך עסקי | תבנית n8n | צמתים עיקריים | API ישראלי |
|-----------|-----------|---------------|------------|
| התאמת חשבוניות | Schedule Trigger -> HTTP -> Compare -> Update | Schedule Trigger, HTTP Request, IF, Code | Morning (חשבונית ירוקה) API |
| סיווג תנועות בנק | Schedule Trigger -> Code -> Spreadsheet | Schedule Trigger, Code, Google Sheets | israeli-bank-scrapers |
| סנכרון נתוני ממשלה | Schedule Trigger -> HTTP -> Transform -> DB | Schedule Trigger, HTTP Request, Code, Postgres | data.gov.il CKAN API |
| הודעות SMS | Trigger -> Code -> HTTP | Webhook, Code, HTTP Request | 019 Telzar / InforUMobile API |
| טיפול ב-webhooks של תשלומים | Webhook -> Validate -> Process | Webhook, IF, Code, HTTP Request | Cardcom / Tranzila / Grow by Meshulam |
| תזמון מותאם חגים | Schedule Trigger -> HTTP -> IF -> Execute | Schedule Trigger, HTTP Request, IF, Code | Hebcal API |
| תהליך אישור רב-שלבי | Webhook -> Wait -> IF -> Notify | Webhook, Wait, IF, HTTP Request | Slack + שער SMS |
| סיווג חכם עם AI | Schedule Trigger -> Code -> AI Agent -> DB | Schedule Trigger, Code, AI Agent, Postgres | israeli-bank-scrapers + LLM |
| ציות לרפורמת חשבוניות | Webhook -> Code -> HTTP -> HTTP | Webhook, Code, HTTP Request | Morning API + מספרי הקצאה |
**איך בוחרים:**
- אם התהליך רץ לפי לוח זמנים, מתחילים עם Schedule Trigger ובודקים אם צריך השהיה בשבת/חגים (שלב 4)
- אם התהליך מגיב לאירועים חיצוניים (אישור תשלום, הגשת טופס), מתחילים עם Webhook trigger
- אם התהליך מעבד טקסט בעברית, מוסיפים Code node בתחילת הצינור לטיפול בקידוד ו-RTL (שלב 3)
- אם התהליך צריך סיווג או סיכום חכם, משתמשים ב-AI Agent node (שלב 7)
### שלב 2: חיבור API ישראליים ב-n8n
#### Morning (חשבונית ירוקה) API
Morning (לשעבר חשבונית ירוקה, Green Invoice) משתמש ב-API key + secret לקבלת JWT token. זה לא OAuth2. הגדרת HTTP Request node:
```
Method: POST
URL: https://api.greeninvoice.co.il/api/v1/account/token
Headers:
Content-Type: application/json
Body:
{
"id": "{{$env.GREEN_INVOICE_API_KEY}}",
"secret": "{{$env.GREEN_INVOICE_API_SECRET}}"
}
```
התגובה מכילה JWT token שתקף ל-60 דקות. שומרים אותו ומעבירים לבקשות הבאות:
```
Authorization: Bearer {{$json.token}}
```
**רפורמת החשבוניות 2026 (הורדת סף):** חשבוניות מס מעל הסף דורשות מספר הקצאה מרשות המסים. הסף יורד במהלך 2026:
| תאריך כניסה לתוקף | סף |
|--------------------|-----|
| 1 בינואר 2026 | 10,000 ש"ח |
| **מ-1 ביוני 2026 ואילך** | **5,000 ש"ח** |
המשטר החל במאי 2024 (25,000 ש"ח), ובינואר 2025 ירד ל-20,000. תהליך שנוגע בחשבוניות היסטוריות חייב לבחון כל חשבונית מול הסף שהיה בתוקף בתאריך שלה עצמה; הטבלה המלאה נמצאת ב-`references/israeli-api-endpoints.md`.
**שני כללים נפרדים חלים כאן, ותהליך שמערבב ביניהם שגוי.** חובת המוכר יושבת בסעיף 47(א2)(1) לחוק מע"מ, קמה רק לפי דרישת הקונה, ואינה חלה על עסקה בשיעור אפס: "ובעסקה שסכומה, בלא המס, עולה על הסכום האמור בסעיף 38(א1), חייב הוא לעשות כן לפי דרישת הקונה; הוראות סעיף קטן זה יחולו לעניין חשבונית מס שהוצאה בשל עסקה שהמס שחל לגביה אינו בשיעור אפס". שלילת ניכוי מס התשומות מהקונה יושבת בסעיף 38(א1), ואין בה שום תנאי של דרישת הקונה: "לא יותר ניכוי מס התשומות הכלול בחשבונית מס שסכומה, בלא המס, עולה על 5,000 שקלים חדשים (מינואר 2026 ועד מאי 2026: 10,000 שקלים חדשים) ושאינה כוללת מספר שהקצה לה המנהל". לכן צומת ולידציה לא יכול להתייחס ל"הקונה לא ביקש" כאישור. שימו לב למילה `עולה על` בחוק: חשבונית שיושבת בדיוק על 5,000 נמצאת מחוץ לכלל, אז השתמשו ב-`>` ולא ב-`>=`.
**הסף נמדד לפני מע"מ.** משווים אותו מול השדה `amount` של Morning ולא מול `totalAmount`. במע"מ של 18% ההפרש בין שני השדות הוא כ-900 ש"ח סביב קו ה-5,000, ולכן השוואה מול השדה הלא נכון מסווגת שגוי חשבוניות בטווח הזה. מעבר ליוני 2026 לא נחקקה הורדה נוספת.
בנו את בדיקת הסף כמשתנה ב-workflow ולא כמספר קשיח. **המנגנון שבו מסמך של Morning מקבל מספר הקצאה אינו מתועד בסקיל הזה**, כי תיעוד ה-API של Morning מוגש כאפליקציית JS שמחזירה 200 לכל נתיב, ולכן אף שם שדה לא ניתן לאימות. אל תמציאו שם שדה ואל תניחו שהוא זהה ל-EZCount. בררו מול Morning, ובינתיים טפלו במסמך שנוצר בלי מספר הקצאה כמצב שדורש התערבות אנושית.
**סכומים בשקלים עשרוניים (לא באגורות).** כשיוצרים מסמכים, `price: 50` זה 50 ש"ח, לא 50 אגורות. אין צורך להכפיל או לחלק ב-100.
נקודות קצה נפוצות של Morning API:
| נקודת קצה | Method | שימוש |
|-----------|--------|-------|
| `/api/v1/documents/search` | POST | חיפוש חשבוניות לפי תאריך, לקוח, סטטוס |
| `/api/v1/documents` | POST | יצירת חשבונית/קבלה חדשה |
| `/api/v1/clients/search` | POST | חיפוש לקוח לפי שם או מספר עוסק |
| `/api/v1/documents/payments/search` | POST | שליפת רשומות תשלום להתאמה |
| `/api/v1/businesses/me` | GET | מידע על העסק הנוכחי |
קודי סוגי מסמכים לשדה `type`:
| קוד | סוג מסמך |
|-----|----------|
| 10 | הצעת מחיר |
| 305 | חשבונית מס |
| 320 | חשבונית מס / קבלה |
| 330 | חשבונית זיכוי / זיכוי |
| 400 | קבלה |
למידע מפורט עיינו ב-`references/israeli-api-endpoints.md`.
#### EZCount (EasyCount) API
EZCount (נכתב גם EasyCount) הוא חלופה פופולרית ל-Morning לעוסקים קטנים. ה-API למסמכים הוא REST עם payload JSON, אימות דרך `api_key` + `api_email` בגוף הבקשה (לא OAuth, לא Bearer).
```
Method: POST
URL: https://api.ezcount.co.il/api/createDoc
Headers:
Content-Type: application/json
Body:
{
"api_key": "{{$env.EZCOUNT_API_KEY}}",
"api_email": "{{$env.EZCOUNT_API_EMAIL}}",
"developer_email": "you@example.com",
"type": 320,
"customer_name": "שם הלקוח",
"customer_email": "client@example.com",
"item": [{ "details": "שירותי ייעוץ", "amount": 1, "price": 500, "vat_type": "INC" }]
}
```
קודי סוגי מסמכים תואמים לקודי רשות המסים שבהם משתמש Morning (305 / 320 / 330 / 400). כמו ב-Morning, **הסכומים בשקלים עשרוניים, לא באגורות**. ברירת המחדל של `vat_type` היא `PRE` (מחיר לפני מע"מ), ו-`INC` למחיר כולל מע"מ.
**עיכוב ההקצאה מגיע כסטטוס 417, לא כשדה שאפשר לנסות שוב.** בתיעוד של EZCount כתוב: "When the document is waiting for the Tax Authority allocation number we will return status `417`". אין שדה `allocation_status`, וניסיון חוזר לא מנקה את המצב. במקרה של 417 התיעוד נותן ארבע אפשרויות: לוותר על מספר ההקצאה, לבטל את המסמך, להגיש השגה נוספת, או לבצע היפוך חיוב (ביטול והפקה מחדש בשיעור אפס, כך שהקונה מפיק חשבונית עצמית). התפצלו על ה-417, הציגו למשתמש את הודעת רשות המסים, ותנו לאדם לבחור, כי לשלוש מתוך ארבע האפשרויות יש השלכות מס. שימו לב גם למגבלת הקצב: 250 בקשות ב-10 שניות, והבקשות חייבות להישלח בזו אחר זו ולא במקביל.
EZCount ו-Morning מפיקים את אותו פלט משפטי (חשבוניות מס מסולקות), אז הבחירה ביניהם תפעולית ולא טכנית. בחרו EZCount אם הלקוח כבר על המערכת החשבונאית של EasyCount, אחרת ל-Morning יש תיעוד API עשיר יותר.
#### israeli-bank-scrapers דרך Code Node
ל-n8n אין צומת מובנה לבנקים ישראליים. החבילה `israeli-bank-scrapers` היא ספריית Node.js ו**לא כלי CLI**, ולכן היא רצה בתוך Code node דרך `createScraper()`. דורשת Node.js >= 22.22.2. שלושה דברים חוסמים אותה, לפי הסדר שבו הם נתקלים: הגדרת `NODE_FUNCTION_ALLOW_EXTERNAL=israeli-bank-scrapers` **על ה-task runner** כדי ש-`require()` יצליח; דרך עובדת להעברת הסודות (ראו למטה, "credential store" לא עובד מתוך Code node); ומפתחות ההתחברות הנכונים לכל בנק. המפתח הוא `password` ולעולם לא `userPassword`, והשדה הראשון שונה מבנק לבנק (`userCode` בהפועלים, `username` בלאומי/מזרחי/מקס, `id` + `num` בדיסקונט/מרכנתיל, `id` + `card6Digits` בישראכרט/אמקס). קראו את `SCRAPERS[companyId].loginFields` במקום להניח מבנה.
**דרך הסודות דורשת בחירה מודעת, כי "שלפו מה-credential store" אינו בר-מימוש בתוך Code node.** ל-Code node אין credentials מוגדרים, ולכן אין שם `$credentials`, ובנפרד `$env` חסום כברירת מחדל ב-2.x. שלוש האפשרויות האמיתיות: להגדיר `N8N_BLOCK_ENV_ACCESS_IN_NODE=false` על ה-task runner ולהישאר עם `$env`; להעביר את הסוד מצומת קודם שנושא credential, בידיעה שהוא מופיע אז בנתוני ההרצה; או external secrets בתוכנית enterprise.
**חסימת Cloudflare (2026):** זיהוי בוטים חוסם דפדפנים headless באמקס ובישראכרט. הפורק המתוחזק `@sergienko4/israeli-bank-scrapers` עוקף זאת עם Camoufox.
**שקלו אם סריקה היא בכלל הדרך הנכונה.** הדפוס הזה שומר את פרטי ההתחברות החיים של הלקוח לבנק כדי שדפדפן headless יתחבר בשמו, וזה עומד בניגוד לתנאי השימוש של רוב הבנקים בישראל בנוגע לשיתוף פרטי התחברות. החלופה המורשית לפי חוק שירות מידע פיננסי (Open Banking) קיימת והיא הדרך שניתן להגן עליה בכל מוצר שפונה ללקוחות. קוד לדוגמה, רשימת ה-`CompanyTypes` המלאה וטבלת שדות ההתחברות לכל בנק נמצאים ב-`references/israeli-api-endpoints.md`.
#### data.gov.il CKAN API
נתונים פתוחים של ממשלת ישראל דרך CKAN API:
```
GET https://data.gov.il/api/3/action/datastore_search
Parameters:
resource_id: <resource-guid>
q: <search-term>
limit: 100
offset: 0
```
מזהי משאבים שימושיים:
| מסד נתונים | Resource ID | תוכן |
|-----------|-------------|------|
| רשם העמותות | be5b7935-3922-45d4-9638-08871b17ec95 | עמותות רשומות |
| סטטיסטיקת יבוא/יצוא | משתנה | נתוני מסחר לפי קוד HS |
ה-API מחזיר שמות שדות בעברית. משתמשים ב-Code node לנרמול המפתחות לאנגלית לפני עיבוד המשך.
#### WhatsApp Business Cloud
ל-n8n יש צמתים ייעודיים: **`n8n-nodes-base.whatsApp`** ("WhatsApp Business Cloud", typeVersion 1.1, credential בשם `whatsAppApi`) לשליחה, ו-**`n8n-nodes-base.whatsAppTrigger`** לקליטה. פעולות: Send, Send Template, Send and Wait for Response.
שני כללים של מטא שוברים את התהליכים האלה, ושניהם לא בשליטת n8n. **חלון שירות הלקוחות של 24 שעות:** משתמש ששולח לכם הודעה או מתקשר פותח חלון של 24 שעות שבתוכו הודעות חופשיות נמסרות; מחוצה לו רק תבנית מאושרת מראש תגיע. תהליך מתוזמן ישראלי רץ מחוץ לחלון מעצם הגדרתו, ולכן חייב Send Template. **תבניות דורשות אישור מראש** באחת משלוש קטגוריות, Marketing, Utility או Authentication, ותבניות בעברית נבדקות כמו כל תבנית, אז אשרו מוקדם. התמחור הוא לפי הודעה שנמסרה (התמחור לפי שיחה הסתיים ב-1.7.2025), ולכן לולאת פיזור עולה כסף על כל פריט.
#### שערי SMS ישראליים
| שער | Host | אימות | מתאים ל |
|-----|------|-------|---------|
| 019 טלזר | `019sms.co.il` | Bearer token, או שם משתמש + סיסמה | דיוור המוני, טרנזקציוני |
| InforUMobile | `capi.inforu.co.il` | Bearer token (עם רשימת IP מורשים) | OTP, טרנזקציוני |
| Nexmo/Vonage IL | `rest.nexmo.com` | API key + secret | בינלאומי + מקומי |
**שער 019 מחזיר HTTP 200 גם כשהשליחה נכשלת.** כשל אימות חוזר כ-`200 {"status":3,"message":"Username or password is incorrect..."}`, ולכן HTTP Request node בהגדרות ברירת מחדל מפרש את זה כהצלחה והתהליך ממשיך בלי שנשלחה הודעה. הפעילו **Always Output Data** והתפצלו על `$json.status` (0 = נשלח) עם IF node, לעולם לא על קוד ה-HTTP. שער InforUMobile אוכף בנוסף רשימת IP מורשים מעבר לטוקן, אז הוסיפו את ה-IP היוצא של n8n גם שם וגם אצל Cardcom ו-Tranzila.
מספרי טלפון חייבים להיות בפורמט בינלאומי `972XXXXXXXXX` (בלי האפס המוביל), והנרמול חייב **לאמת** ולא רק לעצב מחדש: קידומת קווית ישראלית (02/03/04/08/09, 072-077) ומספרי 1-800 או *NNNN אינם יכולים לקבל SMS בכלל, ולכן החלפה עיוורת של `0` ב-`972` מעוותת אותם בשקט. גופי הבקשות, שמות השדות בכל שער ומנרמל שבודק קידומת נמצאים ב-`references/israeli-api-endpoints.md`.
#### שלושה כללים ישראליים שחוסמים את כל התהליך
**1. לא כל עסק רשאי להוציא חשבונית מס.** עוסק פטור אינו רשאי להוציא חשבונית מס (סוגים 305 / 320) ואינו רשאי לגבות מע"מ כלל; הוא מוציא קבלה או חשבונית עסקה. בררו את מעמד המע"מ של העסק לפני שבוחרים סוג מסמך ואל תקבעו 305/320 קשיח, אחרת עוסק פטור יגבה מס שלא כדין על מסמך לא תקף. גם עסקאות בשיעור אפס (ייצוא, שירות לתושב חוץ) והפטור באילת מזיזים את השיעור, ולכן `vat_type` הוא החלטה לכל עסקה. המספור רציף ובלתי ניתן לשינוי, חשבונית שגויה מבוטלת בחשבונית זיכוי (330) ולא נמחקת, והמסמך נשמר שבע שנים, אז שמרו את ה-PDF ולא קישור שפג.
**2. דיוור מסחרי אוטומטי מוסדר בחוק.** סעיף 30א לחוק התקשורת (תיקון 40) חל על כל שליחת SMS ווואטסאפ שאפשר לבנות עם הסקיל הזה. סעיף 30א(ב) דורש **הסכמה מפורשת מראש בכתב** לפני השליחה בכלל. סעיף 30א(י)(1) מאפשר פיצוי ללא הוכחת נזק של עד **1,000 ש"ח לכל דבר פרסומת שהתקבל בניגוד לחוק**, וזו בדיוק החשיפה של לולאת דיוור. חלון 24 השעות של מטא הוא כלל של פלטפורמה ולא הכלל המשפטי, ועמידה בו אינה מייתרת הסכמה.
דרישת הסימון תלוית ערוץ ולרוב מיושמת שגוי. הכלל הכללי, סעיף 30א(ה)(1)(א), דורש את המילה `פרסומת` בתחילת ההודעה. אבל סעיף 30א(ה)(2) הוא חריג ל-SMS: בדבר פרסומת שמשוגר בהודעת מסר קצר המפרסם "יציין בדבר הפרסומת רק את שמו ואת דרכי יצירת הקשר עמו לצורך מתן הודעת סירוב". אל תוסיפו קידומת `פרסומת` לתבנית SMS מתוך הנחה שהכלל הכללי חל.
**3. הנתונים שהתהליכים האלה נוגעים בהם הם מידע אישי מוסדר.** תעודת זהות שמגיעה מ-callback של תשלום, פרטי התחברות חיים לבנק, ותיאורי תנועות שמוזרמים למודל שפה של צד שלישי, כולם נכנסים לחוק הגנת הפרטיות כפי שתוקן (תיקון 13, בתוקף מאוגוסט 2025). צמצמו את מה שאתם שומרים, התייחסו לגיליון הגוגל כמאגר מידע מוסדר עם כלל שמירה, ואל תשלחו מידע מזהה של לקוח למודל זר בלי בסיס חוקי.
### שלב 3: טיפול בנתונים בעברית ב-n8n
צמתי Code מעבדים מחרוזות ב-UTF-8, ולכן עברית עובדת נטיבית. הבעיות מתחילות בגבולות: ייצוא CSV דורש UTF-8-BOM אחרת אקסל קורא ANSI, תגובות HTTP Request דורשות קידוד תגובה מפורש, גוף מייל דורש `<div dir="rtl">`, data.gov.il מחזיר מפתחות JSON בעברית שכדאי לנרמל ב-Code node, ואורך מחרוזת חייב `Array.from(str).length` ולא `.length`. ייצואים ישנים מבנקים ומתוכנות הנהלת חשבונות ישראליות הם לרוב Windows-1255 ולא UTF-8, וה-BOM לא פותר את זה, ושמות שמגיעים מלקוחות עלולים לשאת תווי בקרה דו-כיווניים שמסדרים מחדש ויזואלית סכום על חשבונית מודפסת.
עיצוב מטבע דרך `Intl.NumberFormat('he-IL', { style: 'currency', currency: 'ILS' })`. מסמכים ישראליים משתמשים ב-DD/MM/YYYY: Morning מחזיר ISO 8601 אבל מאגרי ממשלה מחזירים DD/MM/YYYY כמחרוזת, אז פרסרו במפורש ואל תסמכו על `new Date(s)`. פונקציות עזר, מפת חודשים בעברית ומנרמל טלפונים שבודק קידומת נמצאים ב-`references/israeli-api-endpoints.md`.
### שלב 4: תזמון מותאם שבת
תהליכים עסקיים בישראל לא צריכים לרוץ בשבת (כניסת שבת ביום שישי עד מוצאי שבת) ובחגים. ל-Schedule Trigger node של n8n אין תמיכה מובנית בזה, אז בונים צומת בדיקה בתחילת כל תהליך מתוזמן.
**ארכיטקטורה:** Schedule Trigger -> HTTP Request (Hebcal) -> IF (שבת?) -> המשך או עצירה
קריאה ל-Hebcal API ב-HTTP Request node:
```
GET https://www.hebcal.com/shabbat?cfg=json&geonameid=293397&M=on
```
`geonameid=293397` זה תל אביב; ירושלים 281184, חיפה 294801, באר שבע 295530. הדלקת נרות היא 40 דקות לפני השקיעה בירושלים, 30 בחיפה ובזיכרון יעקב, 18 בשאר הערים, ולכן ה-geonameid נושא משקל. Code node לחסימת התהליך:
```javascript
const now = new Date();
const items = $input.first().json?.items;
// כישלון סגור: אם אי אפשר לקרוא את הלוח, אנחנו לא יודעים, ולכן לא רצים.
if (!Array.isArray(items) || items.length === 0) return [];
// מזווגים כל הדלקת נרות עם ההבדלה הראשונה שאחריה. לקיחת
// items.find('candles') ו-items.find('havdalah') שגויה: בשאילתה שנשלחת
// בתוך חג, Hebcal מחזיר את ההבדלה של אותו חג לפני הדלקת הנרות של החג
// הבא, ולכן תנאי הטווח לעולם לא מתקיים והתהליך רץ ביום כיפור.
const ev = items.filter(i => i.category === 'candles' || i.category === 'havdalah')
.map(i => ({ cat: i.category, at: new Date(i.date) }))
.sort((a, b) => a.at - b.at);
for (let k = 0; k < ev.length - 1; k++) {
if (ev[k].cat === 'candles' && ev[k + 1].cat === 'havdalah'
&& now >= ev[k].at && now <= ev[k + 1].at) return [];
}
if (ev.length && ev[0].cat === 'havdalah' && now <= ev[0].at) return [];
return $input.all();
```
**אל תזרקו את העבודה בשקט.** הפקודה `return []` מסיימת את הענף, ולכן חשבונית או הודעה ללקוח שאמורות לצאת בשבת אובדות ולא נדחות. לכל דבר שלקוח ממתין לו, הכניסו את הפריטים לתור (workflow static data או טבלה) ורוקנו אותו בתהליך נפרד אחרי צאת השבת.
לחגים יהודיים, שאילתה ל-Hebcal holidays API:
```
GET https://www.hebcal.com/hebcal?v=1&cfg=json&year=now&month=x&maj=on&mod=on&i=on
```
מסננים פריטים עם `yomtov: true`. שני הפרמטרים קריטיים ושניהם נכשלים בשקט: הערך `month=now` אינו חוקי ו-Hebcal מחזיר HTTP 200 עם `"items": []`, ולכן החסימה לעולם לא מופעלת; ובלי `i=on` Hebcal מחזיר את לוח התפוצות, 13 ימי יום טוב ל-2026 מול 8 בישראל, ומשבית את העסק בחמישה ימי עבודה רגילים. אמתו מול שדה ה-`title` בתשובה: `Hebcal Israel <שנה>` או `Hebcal <עיר> <שנה>`, לעולם לא `Hebcal Diaspora`.
**קרון של ימי חול אינו מייתר את החסימה הזו:** שישה מתוך שמונת ימי היום טוב של 2026 בישראל נופלים בין ראשון לחמישי, ויום כיפור ביניהם. בקובץ `references/shabbat-cron-patterns.md` נמצאים הדפוסים המתוקנים, חישוב התאריך בשעון ישראל שנדרש לערב חג, ומקרי הצומות וחול המועד שהחסימה הזו מתעלמת מהם.
### שלב 5: Webhooks של שערי תשלום ישראליים
שערי תשלום ישראליים שולחים תוצאות עסקאות דרך webhooks. מגדירים Webhook nodes ב-n8n לקליטה ועיבוד.
שלושה שערים מכסים כמעט את כל תעבורת הכרטיסים בישראל, וכל אחד מוסר את ה-callback אחרת. טבלאות השדות המלאות, נתיבי הבסיס וזרימת ביט נמצאות ב-`references/israeli-api-endpoints.md`; מה שקובע נכונות נמצא כאן.
| שער | צורת ה-callback | בדיקת הצלחה | מפתח דה-דופליקציה |
|---|---|---|---|
| Cardcom v11 | POST של `LowProfileResult` ל-`WebHookUrl` שלכם | **`ResponseCode == 0`** | `TranzactionId` |
| Tranzila | פרמטרים ב-GET | `Response == '000'` | `index` |
| Grow by Meshulam | POST בפורמט `multipart/form-data` (לא JSON) | התאמת `webhookKey` למפתח השמור, ואז קריאה חוזרת | `asmachta` |
**השדה `ReturnValue` אינו שדה סטטוס של Cardcom.** במפרט v11 של Cardcom עצמה הוא מתואר כ-"A string of data to save on the transaction, usually send your unique order Id, you will get it back in the WebHook URL", כלומר ערך שאתם שולחים וחוזר אליכם. התפצלות עליו מאשרת עסקאות שנדחו. שדה `DealResponse` לא קיים כלל ב-v11, והשדות `InternalDealNumber` / `CardOwnerID` / `NumOfPayments` הם שמות של ה-API הישן שאינם מופיעים באובייקט התוצאה, ולכן מפתח דה-דופליקציה שבנוי על `InternalDealNumber` תמיד `undefined` והשמירה מפני חשבונית כפולה אף פעם לא פועלת. תעודת הזהות ומספר התשלומים יושבים תחת `TranzactionInfo` בשמות `CardOwnerIdentityNumber` ו-`NumberOfPayments`.
**לעולם אל תפיקו מסמך ישירות מגוף ה-callback.** כתובת ה-webhook פומבית ואף אחד משלושת השערים האלה לא חותם על המטען, ולכן POST מזויף עם סכום סביר יגרום לתהליך שלכם להפיק חשבונית מס אמיתית ולשרוף מספר הקצאה אמיתי. קראו קודם את העסקה מחדש בצד השרת (Cardcom לפי `LowProfileId` / `TranzactionId`, Grow דרך `getPaymentProcessInfo`, Tranzila לפי `index`), אמתו את הסכום, ורק אז הפיקו. ב-Grow צריך בנוסף לקרוא ל-`approveTransaction` כדי לסגור את התשלום.
ביט אינו API עצמאי: מגיעים אליו דרך Tranzila v2 (`bit: true` בעמוד התשלום) או דרך Grow עם ביט מופעל, והוא מגיע באותה זרימת webhook עם `transactionType` שונה.
#### אופני אימות ל-Webhook
צומת Webhook של n8n תומך בארבעה אופני אימות. לאור שרשרת ה-CVE של 2026 סביב webhooks לא מאומתים, "None" על webhook ציבורי הוא למעשה פרצת אבטחה. בכל זרימת תשלום או טופס ציבורי בחרו אחד מהשלושה האחרים:
| אופן | איפה מגדירים | מתי להשתמש |
|------|--------------|------------|
| None | dropdown "Authentication" בצומת Webhook | רק בדיקות מקומיות, אסור בפרודקשן |
| Basic Auth | Generic Credential | webhook פנימי מאחורי VPN; עובד עם כל לקוח HTTP |
| Header Auth | credential מסוג Header Auth (למשל `X-API-Key: <token>`) | ברירת המחדל ל-callbacks של שערי SMS ו-webhooks פנימיים |
| JWT Auth | credential מסוג JWT (HMAC HS256/384/512 או RSA/ECDSA דרך PEM) | אינטגרציות בין-ארגוניות שבהן הקורא כבר מנפיק JWT |
**אבל עבור שלושת השערים הישראליים, HMAC אינו הבקרה הנכונה.** אף אחד מהם לא חותם על המטען: ל-Cardcom v11 אין שדה חתימה כלל, ו-Grow שולח `webhookKey` משותף בתוך הגוף. מה שבאמת מגן עליכם הוא קריאה חוזרת של העסקה בצד השרת לפני הפקת מסמך כלשהו, כמתואר למעלה.
**אימות חתימת HMAC (לשולחים שבאמת חותמים):** ל-n8n אין מאמת HMAC מובנה, אז הוא נכתב ב-Code node. שלושה דברים שוברים את הגרסה הנאיבית: צומת Webhook פולט `{ json: { headers, params, query, body } }`, ולכן `$input.first().headers` מחזיר `undefined`; `$env` חסום בתוך Code nodes כברירת מחדל ב-2.x, ולכן הסוד יוצא `undefined` בשקט; ו-`JSON.stringify(body)` אינו מה שהשולח חתם עליו, כי סדר המפתחות והרווחים שונים. הקוד המלא, כולל טיפול ב-raw body, נמצא ב-`references/webhook-auth-patterns.md`.
**הסתייגות JWT, מתוקנת:** ה-JWT Auth של n8n **כן** אוכף את `exp`. הוא קורא ל-`jwt.verify` מ-`jsonwebtoken`, שדוחה טוקן שפג תוקף כברירת מחדל. גרסה קודמת של הסקיל טענה אחרת וזו הייתה טעות. מה שהוא לא בודק זה `iss` ו-`aud`, כי לא מועברים לו `issuer` ו-`audience`. הוסיפו בדיקת claims לשני אלה בלבד, ורק אחרי שאופן האימות JWT Auth כבר אימת את החתימה.
### שלב 6: שיקולי אירוח עצמי
#### קו האבטחה של n8n 2.x ונעילת גרסה
גרסה n8n 2.0 שוחררה בדצמבר 2025; הגרסה היציבה הנוכחית היא 2.38.7 נכון לספטמבר 2026. **נעלו תג של 2.32.1 ומעלה** ולעולם לא `n8nio/n8n:latest`. שלוש חולשות ברמת CRITICAL (CVE-2026-44789 זיהום prototype ב-HTTP Request node שמוביל ל-RCE, CVE-2026-44790 קריאת קבצים שרירותית בצומת Git, CVE-2026-44791 עקיפת הטלאי בצומת XML) פורסמו ב-14.5.2026 ותוקנו ב-2.22.1, ותיקונים נוספים ברמת HIGH של דליפת credentials ובריחה מארגז החול נחתו לאורך 2.31.5 ו-2.32.1. חולשת CVE-2026-44789 יושבת בדיוק על הנתיב הקריטי של הסקיל הזה, כי כל אינטגרציה ישראלית כאן היא HTTP Request node, וכל זרימת תשלום מוסיפה Webhook ציבורי.
שתי הגדרות מהשינויים של 2.0 חוסמות את הקוד בסקיל הזה, וכשה-task runners פעילים (ברירת המחדל ב-2.0) שתיהן שייכות ל-**runner** ולא לקונטיינר הראשי:
```
NODE_FUNCTION_ALLOW_BUILTIN=crypto
NODE_FUNCTION_ALLOW_EXTERNAL=israeli-bank-scrapers
```
בלי הראשון, מאמת ה-HMAC ב-`references/webhook-auth-patterns.md` זורק שגיאה על `require('crypto')`. גם `N8N_BLOCK_ENV_ACCESS_IN_NODE` מוגדר `true` כברירת מחדל ב-2.x, ולכן `$env.*` בתוך Code node מחזיר ריק בשקט. שימו לב ש-credential store אינו הפתרון כאן (ל-Code node אין credentials); בחרו אחת משלוש הדרכים בשלב 2.
**גרסה n8n 3.0 נוחתת באוקטובר 2026** ומסירה התקנות npm לאירוח עצמי (Docker בלבד), את הצמתים Function, Function Item ו-Item Lists, את העוזר `$getPairedItem`, ואת AI Agent node בגרסה 1 יחד עם כל מצבי הסוכן הישנים. בנו את שלב 7 על Tools Agent הנוכחי.
בקובץ `references/n8n-version-migration.md` נמצאים היסטוריית ה-CVE המלאה, טבלת השינויים השוברים של 2.0, ההפעלה מחדש של Execute Command, הרשימה המלאה של 3.0, ואפשרויות האירוח הישראליות.
#### הגדרות אירוח עצמי שחשובות בישראל
הגדירו **גם** `GENERIC_TIMEZONE=Asia/Jerusalem` וגם `TZ=Asia/Jerusalem`. בלעדיהן צמתי Schedule Trigger רצים לפי UTC וחישובי השבת מוזזים ב-2-3 שעות; שעון הקיץ בישראל מתחיל ביום שישי שלפני יום ראשון האחרון של מרץ ומסתיים ביום ראשון האחרון של אוקטובר. הגדירו `N8N_ENCRYPTION_KEY` יציב כדי שה-credential store ישרוד הפעלות מחדש, נעלו את תג ה-image, ושימו את משתני המודולים של ה-Code node על שירות ה-task runner. קובץ Compose מוכן וטבלת האירוח הישראלי נמצאים ב-`references/n8n-version-migration.md`.
### שלב 7: צמתי AI Agent של n8n לתהליכים ישראליים
n8n 2.x מגיע עם אינטגרציית LangChain מובנית (קבוצת "Advanced AI"): Tools Agent, צמתי זיכרון, צמתי Vector Store ל-RAG (Pinecone, Qdrant, Supabase pgvector), וצמתי Model עבור OpenAI, Anthropic ומודלים מקומיים דרך Ollama. בנו על **Tools Agent**: גרסה 3.0 מסירה את AI Agent node בגרסה 1 יחד עם המצבים SQL, Conversational, OpenAI Functions, Plan-and-Execute ו-ReAct.
**אל תקבעו שם מודל קשיח מתוך מדריך.** צומת Anthropic Chat Model הנוכחי מושך את רשימת המודלים מה-API של Anthropic בזמן ריצה ולא מרשימה נעולה, ולכן הרשימה זזה בלי גרסה חדשה של n8n. בחרו לפי הדרישה (מודל frontier מדרג הביניים לסיווג בעברית, החזק ביותר הזמין לטקסט משפטי עברי ארוך, בעל הלטנסי הנמוך ביותר לצ'אט בזמן אמת, ומודל מקומי רב-לשוני על VPS ישראלי כשמידע אישי חייב להישאר בישראל), וקחו את מה שהצומת מציע היום. ל-RAG על קורפוס עברי השתמשו במודל embedding רב-לשוני (Cohere `embed-multilingual-v3.0` או OpenAI `text-embedding-3-large`); המודל `text-embedding-ada-002` חלש בעברית. שימו לב שהזרמת תיאורי תנועות של לקוחות למודל זר היא החלטת פרטיות ולא רק החלטת איכות.
**צמתי MCP.** הצומת MCP Client Tool (`@n8n/n8n-nodes-langchain.mcpClientTool`) מתחבר כצומת משנה כדי ש-AI Agent יקרא לכלים בשרת MCP חיצוני, למשל השרתים `hebcal`, `israeli-bank` ו-`data-gov-il` של agentskills.co.il. הצומת MCP Server Trigger (`@n8n/n8n-nodes-langchain.mcpTrigger`) חושף תהליך n8n עצמו ככלי MCP עבור Claude Desktop, Cursor או Windsurf, ומגרסה 2 הוא מציע גם n8n User Auth (OAuth2). צומת `toolMcp` לא קיים: זו הזיה נפוצה ו-n8n דוחה JSON של תהליך שמשתמש בו. דוגמה מלאה של מסווג תנועות בעברית נמצאת ב-`references/n8n-workflow-authoring.md`.
### שלב 8: בחירת פלטפורמה, מבנה ה-JSON ו-credentials
בחרו ב-n8n על פני Make.com או Zapier כשצריך אירוח עצמי בגלל מיקום נתונים בישראל, אוטומציות ללא הגבלה, או גישה מלאה לקוד בשביל המוזרויות של API ישראליים. אף אחת מהשלוש לא מגיעה עם צמתים ישראליים מובנים, ולכן העבודה נעשית ב-HTTP Request וב-Code בכל מקרה; רק את n8n אפשר לארח באזור ישראלי.
תהליכים הם JSON: מערך `nodes` (לכל אחד `name` ייחודי שמשמש כמפתח החיבורים, `type` כמו `n8n-nodes-base.httpRequest`, `typeVersion`, `parameters` ו-`position`) ואובייקט `connections` שממופתח לפי שם צומת המקור. מייבאים דרך `POST /api/v1/workflows` ואז **מפרסמים** לפני הרצה. נעלו את ה-`typeVersion` הנוכחי של כל צומת ולא מספר ישן ממדריך: Schedule Trigger, למשל, נמצא כבר על 1.4 בעוד רוב הדוגמאות מראות 1.2.
סודות נשמרים ב-credential store המוצפן של n8n ולא בתוך ה-JSON, עם חריג אחד שחשוב כאן: ל-Code node אין credentials, ולכן סודות בנק ו-HMAC צריכים אחת מהדרכים שתוארו בשלב 2. גם ל-Morning אין credential נטיבי, אז משרשרים HTTP Request ל-`/account/token` ומעבירים `Authorization: Bearer {{token}}` הלאה, עם רענון בכל הרצה כי ה-JWT פג אחרי 60 דקות.
טבלת ההשוואה המלאה, דוגמת JSON שלמה של תהליך והגדרת ה-credentials לכל שירות נמצאות ב-`references/n8n-workflow-authoring.md`.
## דוגמאות
### דוגמה 1: חיבור Morning ל-n8n להתאמת חשבוניות יומית
המשתמש אומר: "כל בוקר תמשוך את חשבוניות Morning של אתמול ותסמן את אלה שעדיין לא שולמו."
צומת אחר צומת:
1. **Schedule Trigger**: cron `0 6 * * 0-4` (09:00 שעון ישראל חורף, ראשון-חמישי).
2. **HTTP Request, "Get Token"**: `POST https://api.greeninvoice.co.il/api/v1/account/token` עם `{ id, secret }` מה-credentials. פלט: JWT.
3. **HTTP Request, "Search Documents"**: `POST /api/v1/documents/search` עם `Authorization: Bearer {{$json.token}}`, גוף שמסנן `fromDate`/`toDate` לאתמול ו-`type` ל-305/320.
4. **צומת IF**: מתפצל לפי `status` (פתוח מול סגור) כדי להפריד חשבוניות שלא שולמו.
5. **HTTP Request (SMS) או Send Email**: מודיע למנהל החשבונות על חשבוניות שלא שולמו, גוף בעברית עטוף ב-`<div dir="rtl">`.
עטפו את כל התהליך בבדיקת השבת משלב 4 אם הוא לא אמור לרוץ לעולם בחג שנופל באמצע השבוע.
### דוגמה 2: תנועות בנק ל-Google Sheet, מודע לחגים
המשתמש אומר: "תסרוק את חשבון העסק שלי כל לילה ותוסיף תנועות חדשות לגיליון, אבל תדלג על שבת וחגים."
צומת אחר צומת:
1. **Schedule Trigger**: cron לשעת ערב באמצע השבוע.
2. **HTTP Request (Hebcal)** + **Code (בדיקת שבת)** משלב 4: פלט ריק עוצר את ההרצה בשבת/חג.
3. **Code node**: מריץ `israeli-bank-scrapers` דרך `createScraper()` (שלב 2), פריט אחד לכל תנועה.
4. **Code node**: מנרמל תיאורים בעברית, מעצב סכומים עם `Intl.NumberFormat('he-IL', ...)`, מפרסר תאריכים כ-DD/MM/YYYY.
5. **Google Sheets node** (Append): כותב שורות לגיליון הנהלת החשבונות.
6. תהליך **Error Trigger** נפרד תופס הרצה שנכשלה ומתריע (ראו מלכודות נפוצות).
## שרתי MCP מומלצים
שרתי ה-MCP הבאים מהדירקטוריה נותנים לצומת AI Agent נתונים ישראליים חיים לפי דרישה:
- **hebcal**: לוח השנה היהודי וזמני שבת, חלופה לקריאה ל-Hebcal HTTP API בכל תהליך.
- **israeli-bank**: נתוני חשבונות בנק ישראליים, מאפשר לסוכן למשוך תנועות במקום להריץ `israeli-bank-scrapers` ב-Code node.
- **data-gov-il**: נתונים פתוחים של ממשלת ישראל (CKAN), שאילתת מרשמים בלי לבנות HTTP Request nodes ידנית.
## קישורי עזר
| מקור | כתובת | מה לבדוק |
|------|-------|----------|
| תיעוד n8n | https://docs.n8n.io/ | מדריך צמתים, ביטויים, אירוח עצמי |
| שינויים שוברים ב-n8n 2.0 | https://docs.n8n.io/changelog/v20-breaking-changes | Execute Command, NODES_EXCLUDE, חסימת $env, DB שהוסרו |
| הפעלת מודולים ב-Code node | https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/configuration-examples/enable-modules-in-code-node | NODE_FUNCTION_ALLOW_BUILTIN ו-NODE_FUNCTION_ALLOW_EXTERNAL |
| חסימת גישה לצמתים ב-n8n | https://docs.n8n.io/hosting/securing/blocking-nodes/ | תחביר NODES_EXCLUDE / NODES_INCLUDE |
| API של Morning (חשבונית ירוקה) | https://www.greeninvoice.co.il/api-docs | נקודות קצה, סוגי מסמכים, תהליך הקצאה |
| API של Hebcal | https://www.hebcal.com/home/developer-apis | זמני שבת, חגים, ערכי geonameid |
| CKAN API של data.gov.il | https://data.gov.il/api/3/action/help_show?name=datastore_search_sql | datastore_search, resource IDs |
## מלכודות נפוצות
- **סוכנים נועלים `n8nio/n8n:latest` או תג מיושן של 2.1x/2.2x.** גרסה 2.21.4 לבדה נושאת 68 התראות אבטחה שפורסמו, ובהן שלוש ברמת CRITICAL (CVE-2026-44789 זיהום prototype ב-HTTP Request node שמוביל ל-RCE, CVE-2026-44790 קריאת קבצים שרירותית בצומת Git, ו-CVE-2026-44791 עקיפת הטלאי בצומת XML) שתוקנו ב-2.22.1, ועוד תיקונים ברמת HIGH שנחתו לאורך 2.31.5 ו-2.32.1. כל Webhook ציבורי (כל workflow של שער תשלום בסקיל הזה) מרחיב את החשיפה. נעלו תג של 2.32.1 ומעלה, היציב הנוכחי הוא 2.38.7.
- **סוכנים כותבים `@n8n/n8n-nodes-langchain.toolMcp` ל-MCP Client Tool.** צומת כזה לא קיים. השמות הם `mcpClientTool` ו-`mcpTrigger`.
- **סוכנים שולחים הודעת וואטסאפ חופשית מתהליך מתוזמן.** חלון שירות הלקוחות של 24 שעות כבר נסגר, ולכן רק תבנית מאושרת תגיע. השתמשו ב-Send Template ואשרו את התבנית מראש.
- **סוכנים מנסים שוב 417 של EZCount כאילו מדובר בשגיאה חולפת.** 417 אומר שרשות המסים לא הקצתה מספר, וניסיון חוזר לעולם לא ינקה את זה. אין שדה `allocation_status`. התפצלו על ה-417 והציגו את ארבע האפשרויות המתועדות.
- **סוכנים מצטטים את CVE-2026-21858 כסיבה לנעול גרסת 2.x.** מדובר בגישה לקבצים ללא אימות, לא ב-RCE, והיא נוגעת לגרסאות 1.65.0 עד 1.120.x בלבד. קו 2.x מעולם לא היה פגיע. רצפת המינימום המעשית היא 2.32.1.
- **סוכנים קוראים סודות עם `$env` בתוך Code nodes.** הגישה חסומה כברירת מחדל ב-2.x (`N8N_BLOCK_ENV_ACCESS_IN_NODE=true`), ולכן הקריאה מחזירה ריק בשקט וה-node נכשל על ערך לא מוגדר. אבל credential store אינו הפתרון (ל-Code node אין credentials); בחרו אחת משלוש הדרכים בשלב 2.
- **סוכנים קוראים ל-`require()` ב-Code node בלי להתיר את המודול.** הפקודה `require('crypto')` דורשת `NODE_FUNCTION_ALLOW_BUILTIN=crypto`, והפקודה `require('israeli-bank-scrapers')` דורשת `NODE_FUNCTION_ALLOW_EXTERNAL=israeli-bank-scrapers`. כאשר task runners פעילים (ברירת המחדל ב-2.0), שני המשתנים מוגדרים על ה-runner ולא על הקונטיינר הראשי.
- **סוכנים מעבירים `userPassword` ל-israeli-bank-scrapers.** שם השדה הוא `password`, והשדה הראשון משתנה מבנק לבנק: `userCode` בהפועלים, `username` בלאומי/מזרחי/מקס, `id` ו-`num` בדיסקונט/מרכנתיל, `id` ו-`card6Digits` בישראכרט/אמקס. אין מבנה אחיד.
- **סוכנים בונים את שער החגים של Hebcal עם `month=now` ובלי `i=on`.** הערך `month=now` אינו חוקי ומחזיר מערך `items` ריק (HTTP 200, בלי שגיאה), ולכן השער אף פעם לא נסגר. השמטת `i=on` מחזירה את לוח התפוצות, שמשבית תהליכים ישראליים בחמישה ימים שהם ימי עבודה רגילים בישראל. השתמשו ב-`month=x&i=on`.
- **סוכנים סומכים על קוד ה-HTTP של שערי SMS ישראליים.** שער 019 מחזיר HTTP 200 עם `status: 3` בכשל אימות. הסתעפו לפי שדה בגוף התשובה, לא לפי קוד הסטטוס.
- **סוכנים מניחים ש-webhook אחד שווה תשלום אחד.** שלושת השערים שולחים webhooks חוזרים. בצעו דה-דופליקציה לפי `TranzactionId` או `index` או `asmachta` לפני יצירת מסמך, אחרת שליחה חוזרת מפיקה חשבונית כפולה ושורפת מספר הקצאה שני.
- **סוכנים מתפצלים על `ReturnValue` ב-webhook של Cardcom.** זהו מזהה ההזמנה שלכם שחוזר אליכם, לא סטטוס. השתמשו ב-`ResponseCode == 0`, ובצעו דה-דופליקציה לפי `TranzactionId` ולא לפי `InternalDealNumber` שאינו קיים.
- **סוכנים בונים את חסימת השבת עם `items.find('candles')` ו-`items.find('havdalah')`.** בשאילתה שנשלחת בתוך חג, Hebcal מחזיר את ההבדלה לפני הדלקת הנרות הבאה, ולכן התנאי לעולם לא מתקיים והתהליך רץ ביום כיפור. זווגו כל הדלקת נרות עם ההבדלה שאחריה, וכשלו סגור כשאי אפשר לקרוא את הלוח.
- **סוכנים אומרים למשתמש לשמור סודות של Code node ב-credential store.** ל-Code node אין credentials. שחררו `$env` על ה-runner, או העבירו את הסוד מצומת קודם.
- **סוכנים מפיקים חשבונית ישירות מגוף ה-webhook של התשלום.** הכתובת פומבית ואף שער ישראלי לא חותם על המטען. קראו קודם את העסקה מחדש בצד השרת.
- **סוכנים קובעים סוג מסמך 305/320 קשיח.** עוסק פטור אינו רשאי להוציא חשבונית מס ואינו רשאי לגבות מע"מ כלל. בררו את מעמד המע"מ קודם.
- **סוכנים בונים דיוור SMS או וואטסאפ בלי בדיקת הסכמה ובלי הסרה.** תיקון 40 לחוק התקשורת נושא פיצוי של עד 1,000 ש"ח להודעה ללא הוכחת נזק.
- **סוכנים משתמשים ב-UTC כברירת מחדל ל-schedule triggers.** ישראל ב-`Asia/Jerusalem` (UTC+2/+3), ומעבר לשעון קיץ בישראל קורה בתאריכים שונים מארה"ב ואירופה (שעון קיץ מתחיל ביום שישי שלפני יום ראשון האחרון של מרץ, ומסתיים ביום ראשון האחרון של אוקטובר). תמיד להגדיר `GENERIC_TIMEZONE` ולוודא אחרי כל מעבר שעון.
- **סוכנים מפרמטים תאריכים כ-MM/DD/YYYY.** בישראל הפורמט הוא DD/MM/YYYY. כל Code node שמפרסר תאריכים חייב לטפל בזה מפורשות. Morning API מחזיר ISO 8601, אבל מערכות ממשלה מחזירות DD/MM/YYYY כמחרוזות.
- **סוכנים שולחים מספרי טלפון ישראליים עם אפס פותח.** שערי SMS דורשים פורמט בינלאומי (`972XXXXXXXXX`). מספר כמו `050-1234567` חייב להפוך ל-`972501234567`.
- **סוכנים מניחים שמע"מ כלול בסכומים.** חשבוניות ישראליות מציגות בדרך כלל סכומים לפני מע"מ. Morning API מחזיר גם `amount` (לפני מע"מ) וגם `totalAmount` (כולל מע"מ). תמיד לבדוק איזה שדה נדרש. שיעור מע"מ נוכחי: 18% (נכון ל-2026).
- **סוכנים מתעלמים מכך שזמני שבת משתנים לפי עיר.** הדלקת נרות בירושלים 40 דקות לפני השקיעה, בחיפה וזיכרון יעקב 30 דקות, ובתל אביב וכל שאר הערים 18 דקות. זמן קבוע אחד לכל ישראל יגרום לתהליכים לרוץ בשבת בחלק מהערים.
- **Execute Command node מושבת כברירת מחדל ב-n8n 2.0.** תהליכים שהשתמשו ב-Execute Command להרצת סקריפטים (למשל לסריקת בנקים) ייכשלו בשקט אחרי שדרוג ל-n8n 2.0. יש לעבור ל-Code nodes, או להפעיל מחדש דרך דריסת משתנה הסביבה `NODES_EXCLUDE` כך שלא יכיל את `n8n-nodes-base.executeCommand` (אין משתנה `N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE`, זו הזיה נפוצה).
- **סכומים ב-Morning API הם בשקלים, לא באגורות.** ה-API משתמש בשקלים עשרוניים (`price: 50` = 50 ש"ח). אין להכפיל ב-100 או לבצע המרות אגורות. זה שונה מכמה שערי תשלום שמשתמשים באגורות.
- **רפורמת החשבוניות 2026 משפיעה על אוטומציות, הסף יורד ב-1 ביוני 2026.** חשבוניות מס מעל הסף (10,000 ש"ח עד 31 במאי 2026, ואז 5,000 ש"ח החל מ-1 ביוני 2026) שנוצרו דרך API דורשות כעת מספרי הקצאה מרשות המסים. הסף נמדד **לפני מע"מ**, ולכן משווים אותו מול `amount` ולא מול `totalAmount`. תהליכים שמייצרים חשבוניות אוטומטית חייבים לטפל בשלב ההקצאה, אחרת החשבונית לא תקפה לניכוי מס. שמרו את הסף כמשתנה ב-workflow, לא כמספר קשיח. החוק אומר `עולה על`, אז השתמשו ב-`>` ולא ב-`>=`, ואל תתנו לשאלה אם הקונה ביקש להשפיע: סעיף 38(א1) שולל את ניכוי מס התשומות מהקונה בכל מקרה.
- **קיצורי המקלדת בעורך של n8n נשברים תחת פריסת מקלדת בעברית.** הקנבס קורא את `e.key` במקום `e.code`, אז כשמקלדת עברית פעילה `Ctrl+C` מחזיר `e.key = 'ב'` והקיצור נכשל. החליפו את שפת הקלט לאנגלית בזמן עריכה, או השתמשו בפעולות מהתפריט. issue 12569 ב-GitHub של n8n.
- **לעורך הביטויים והטקסט של n8n אין תמיכה native ב-RTL.** טקסט עברי בשדות ביטוי מוצג משמאל לימין, מה שמקשה לקרוא מחרוזות עברית ארוכות ושובר את היישור הויזואלי עם סימני פיסוק סובבים. למחרוזות עברית ליטרליות ארוכות, שמרו אותן במשתני סביבה או ב-static workflow data וקראו להן בשם, במקום להקליד אותן בעורך הביטויים.
- **תהליכים לא מנוטרים נכשלים בשקט בלי Error Trigger.** סריקת בנק מתוזמנת או סנכרון חשבוניות שזורק שגיאה פשוט נעצר, ואף אחד לא יודע עד שהנתונים מיושנים. צרו תהליך נפרד שמתחיל בצומת **Error Trigger** (n8n מנתב כל הרצה שנכשלה אליו) ששולח התראה בעברית ל-Slack או SMS. לכשלים זמניים (חסימות Cloudflare, טוקנים שפגו, rate limit) הפעילו גם **Retry On Fail** ברמת הצומת עם המתנה סבירה, במקום לתת לכל ההרצה למות בכשל הראשון.
## משאבים מצורפים
### מסמכי עזר
- `references/israeli-api-endpoints.md` -- טבלת עזר מלאה של נקודות קצה API ישראליות לתהליכי n8n, כולל Morning (חשבונית ירוקה), data.gov.il, שערי SMS, שערי תשלום ו-Hebcal. עיינו בו בעת הגדרת HTTP Request nodes לשירותים ישראליים.
- `references/webhook-auth-patterns.md` -- קטעי Code node לאימות חתימת HMAC ולבדיקת claims של JWT.
- `references/n8n-version-migration.md` -- היסטוריית CVE מלאה, טבלת השינויים השוברים של 2.0, הרשימה המלאה של 3.0, ואפשרויות אירוח ישראליות.
- `references/n8n-workflow-authoring.md` -- בחירת פלטפורמה, מבנה ה-JSON של תהליך, הגדרת credentials, צמתי AI, ופתרון בעיות מורחב.
- `references/shabbat-cron-patterns.md` -- תבניות תזמון מוכנות מראש מותאמות שבת ל-n8n כולל הגדרות שבועיות, חודשיות ומותאמות חגים עם אינטגרציית Hebcal API. עיינו בו בעת הגדרת כל תהליך מתוזמן שצריך לכבד שבת וחגים.
## פתרון בעיות
| תסמין | סיבה | פתרון |
|---|---|---|
| Morning מחזיר 401 | ה-JWT פג (60 דקות) | רעננו את הטוקן בתחילת כל הרצה; שמרו אותו ב-`$getWorkflowStaticData('global')` עם חותמת זמן ורעננו אחרי 55 דקות |
| עברית משובשת בייצוא CSV | חסר UTF-8 BOM ואקסל קורא ANSI | הגדירו קידוד UTF-8-BOM בצומת Spreadsheet File |
| callbacks של Cardcom לא מגיעים | הכתובת אינה נגישה מבחוץ, או שה-IP לא ברשימה המורשית | HTTPS ציבורי עם SSL תקין, `WEBHOOK_URL` שתואם לכתובת הציבורית, וה-IP של n8n ברשימה המורשית ב-Cardcom |
| Schedule Trigger רץ בשבת | אזור הזמן של השרת הוא UTC, או שהחסימה זיווגה נרות והבדלה לא נכון (שלב 4) | הגדירו `GENERIC_TIMEZONE` ו-`TZ`, הדפיסו `new Date().toString()` ב-Code node לאימות, והשתמשו בחסימה עם הזיווג המסודר |
| israeli-bank-scrapers נכשל ב-Code node | `require()` חסום, סודות undefined, או מפתחות התחברות שגויים | הגדירו `NODE_FUNCTION_ALLOW_EXTERNAL` **על ה-task runner**, בחרו דרך סודות עובדת (שלב 2), וקראו את `SCRAPERS[companyId].loginFields` |
| Cloudflare חוסם סריקה באמקס וישראכרט | זיהוי בוטים על דפדפנים headless | עברו לפורק המתוחזק `@sergienko4/israeli-bank-scrapers` (Camoufox) |
אבחונים מפורטים יותר, כולל התלויות בתמונת ה-runner ורצפת הזיכרון ל-Chromium, נמצאים ב-`references/n8n-workflow-authoring.md`.
SKILL.md
---
name: n8n-hebrew-workflows
description: Build n8n 2.x automation workflows (stable 2.38) with Israeli API integrations including Morning (Green Invoice), EZCount, israeli-bank-scrapers, data.gov.il, SMS gateways, Cardcom v11, Tranzila v2, Grow by Meshulam. Use when user asks to "create n8n workflow for Israeli business", "connect Morning to n8n", "automate hashbonit", "Shabbat-aware schedule trigger", "n8n AI agent", or integrate Israeli payment gateways. Covers Hebrew data handling, NIS formatting, Hebcal scheduling, n8n 2.x security patches (CVE-2026-44789 chain), AI Agent nodes with LangChain + RAG, MCP Client Tool and MCP Server Trigger, Israel Invoice Reform 2026 (allocation numbers, 5,000 NIS threshold from June 2026). Do NOT use for invoice management outside an n8n workflow (use green-invoice-il), general n8n tutorials without Israeli context, or Hebrew NLP (use hebrew-nlp-toolkit).
license: MIT
allowed-tools: Bash(n8n:*) Bash(curl:*) Bash(node:*) Bash(npx:*) Bash(docker:*)
compatibility: Requires n8n 2.32.1 or later (patches the CVE-2026-44789/44790/44791 chain); current stable 2.38.7. Node.js 22.22.2+ for israeli-bank-scrapers. Docker recommended for self-hosting. Works with Claude Code, Cursor, GitHub Copilot, Windsurf, OpenCode, Codex, Gemini CLI.
---
# n8n Hebrew Workflows
## Instructions
### Step 1: Identify the Automation Pattern
Scheduled flows start with a Schedule Trigger and MUST add the Shabbat/holiday gate (Step 4). Event-driven flows (payment confirmations, form submissions) start with a Webhook trigger. Add a Code node early when Hebrew text needs encoding or RTL handling (Step 3), and an AI Agent node for categorization or summarization (Step 7). Field references for every Israeli service named here are in `references/israeli-api-endpoints.md`.
### Step 2: Connect Israeli APIs in n8n
#### Morning (formerly Green Invoice) API
Morning ("hashbonit yeruka" / חשבונית ירוקה) uses API key + secret to obtain a JWT token (NOT OAuth2). Configure HTTP Request:
```
POST https://api.greeninvoice.co.il/api/v1/account/token
Body: { "id": "{{$env.GREEN_INVOICE_API_KEY}}", "secret": "{{$env.GREEN_INVOICE_API_SECRET}}" }
```
The response carries a JWT valid for 60 minutes; pass it on as `Authorization: Bearer {{$json.token}}`.
**Israel Invoice Reform 2026 (threshold step-down):** Tax invoices over the threshold require an allocation number (mispar haktza'a) from the Tax Authority. The threshold drops mid-year:
| Effective | Threshold |
|-----------|-----------|
| Jan 1, 2026 | 10,000 NIS |
| **Jun 1, 2026 onwards** | **5,000 NIS** |
The regime began May 2024 (25,000 NIS), then 20,000 from January 2025, so a workflow touching historical invoices must test each one against the threshold in force on ITS OWN date. Full schedule in `references/israeli-api-endpoints.md`.
**Two separate rules govern this, and conflating them is the classic error.** The SELLER's duty is s.47(א2)(1), arising only on the buyer's demand and not on a zero-rated transaction: "ובעסקה שסכומה, בלא המס, עולה על הסכום האמור בסעיף 38(א1), חייב הוא לעשות כן לפי דרישת הקונה; הוראות סעיף קטן זה יחולו לעניין חשבונית מס שהוצאה בשל עסקה שהמס שחל לגביה אינו בשיעור אפס". The BUYER's loss of the input-VAT deduction is s.38(א1), with NO buyer-request condition: "לא יותר ניכוי מס התשומות הכלול בחשבונית מס שסכומה, בלא המס, עולה על 5,000 שקלים חדשים (מינואר 2026 ועד מאי 2026: 10,000 שקלים חדשים) ושאינה כוללת מספר שהקצה לה המנהל". So never treat "the buyer never asked" as a pass. Note `עולה על` (EXCEEDS): an invoice exactly on 5,000 is outside the rule, so use `>`, not `>=`.
**The threshold is measured BEFORE VAT.** Compare it against Morning's `amount` field, NOT `totalAmount`. At 18% VAT the two differ by a 900 NIS band around the 5,000 NIS line, so comparing the wrong field misclassifies invoices in that range.
Build the threshold as a configurable variable, not a hardcoded number. **Amounts are decimal shekels, not agorot:** `price: 50` means 50 NIS.
Creating and delivering a document is ONE call: no endpoint emails an existing document, so delivery is a property of creation, via `emailContent` and `attachment` on `POST /api/v1/documents`. **Morning's API reference is a JS-only app returning HTTP 200 for every path, so the exact body shape is not verified in this skill.** Do not invent field names or assume they match EZCount's: read `https://www.greeninvoice.co.il/api-docs` in a browser first, and treat a document created without an allocation number as needing human intervention, not a retry.
Common Morning endpoints:
| Endpoint | Method | Use Case |
|----------|--------|----------|
| `/api/v1/documents/search` | POST | Search invoices by date range, client, status |
| `/api/v1/documents` | POST | Create new invoice/receipt |
| `/api/v1/clients/search` | POST | Look up client by name or osek number |
| `/api/v1/documents/payments/search` | POST | Fetch payment records for reconciliation |
| `/api/v1/businesses/me` | GET | Get current business info |
Document type codes: 10 (price quote), 305 (tax invoice), 320 (tax invoice + receipt), 330 (credit note), 400 (receipt). Full endpoint details and response schemas are in `references/israeli-api-endpoints.md`.
#### EZCount (EasyCount) API
EZCount is a common Morning alternative. REST + JSON, authenticated with `api_key` + `api_email` in the body (not Bearer, not OAuth).
```
POST https://api.ezcount.co.il/api/createDoc
Body: { "api_key": "...", "api_email": "...", "developer_email": "you@example.com",
"type": 320, "customer_name": "שם הלקוח", "customer_email": "client@example.com",
"item": [{ "details": "שירותי ייעוץ", "amount": 1, "price": 500, "vat_type": "INC" }] }
```
Document type codes match the Tax Authority numbering used by Morning (305/320/330/400). Amounts are decimal shekels; `vat_type` is `PRE` (price before VAT) by default, `INC` for VAT-inclusive.
**The EZCount allocation hold is an HTTP 417, not a retryable field.** Their docs: "When the document is waiting for the Tax Authority allocation number we will return status `417`." There is no `allocation_status` field and retrying never clears it. On 417 the docs give four options: skip the allocation number, cancel the document, file a further objection, or reverse charge (re-issue at zero-rate VAT so the buyer self-invoices). Branch on the 417, surface the Tax Authority message, and let a human choose, since three of the four have tax consequences. Rate limit: 250 requests per 10 seconds, sequential not parallel.
EZCount and Morning produce the same legal output; pick by whichever suite the user already runs.
#### israeli-bank-scrapers via Code Node
`israeli-bank-scrapers` is a Node.js **library, not a CLI**, so it runs inside a Code node via `createScraper()`. Requires Node.js >= 22.22.2. Three things gate it, in the order they bite: `NODE_FUNCTION_ALLOW_EXTERNAL=israeli-bank-scrapers` **on the task runner** so `require()` resolves; a working secret route (see the note below, "use the credential store" does not work from a Code node); and the correct per-bank credential keys. The key is `password`, never `userPassword`, and the first field differs per bank (`userCode` for Hapoalim, `username` for Leumi/Mizrahi/Max, `id` + `num` for Discount/Mercantile, `id` + `card6Digits` for Isracard/Amex). Read `SCRAPERS[companyId].loginFields` rather than assuming a shape.
**The secret route needs a deliberate choice, because "pull it from the credential store" is not implementable inside a Code node.** The Code node declares no credentials, so there is no `$credentials` there, and `$env` is separately blocked by default in 2.x. The three real options are: set `N8N_BLOCK_ENV_ACCESS_IN_NODE=false` on the task runner and keep `$env`; pass the secret in from a preceding credential-bearing node, accepting that it lands in execution data; or use external secrets on enterprise.
**Cloudflare blocking (2026):** bot detection blocks headless browsers on Amex and Isracard. The maintained fork `@sergienko4/israeli-bank-scrapers` works around it with Camoufox.
**Consider whether scraping is the right route at all.** This pattern stores the customer's live bank login so a headless browser can sign in as them, which sits badly against most Israeli bank terms on credential sharing. The licensed alternative under חוק שירות מידע פיננסי (Open Banking) exists and is the defensible path for anything client-facing. Working sample code, the full `CompanyTypes` list and the per-bank login-field table are in `references/israeli-api-endpoints.md`.
#### data.gov.il CKAN API
```
GET https://data.gov.il/api/3/action/datastore_search?resource_id=<guid>&q=<term>&limit=100
```
Useful resource IDs: Non-Profit Registry (`be5b7935-3922-45d4-9638-08871b17ec95`) for registered amutot; trade statistics by HS code (various IDs). The API returns Hebrew field names; use a Code node to normalize keys to English before downstream processing.
#### WhatsApp Business Cloud
n8n ships first-class nodes: **`n8n-nodes-base.whatsApp`** ("WhatsApp Business Cloud", typeVersion 1.1, credential `whatsAppApi`) for sending, and **`n8n-nodes-base.whatsAppTrigger`** for inbound. Operations: Send, Send Template, Send and Wait for Response.
Two Meta rules break these workflows. **The 24-hour customer service window:** a user messaging or calling you opens a 24-hour window in which free-form messages deliver; outside it only a pre-approved template will, and a scheduled workflow fires outside it by definition, so it must use Send Template. **Templates need prior approval** in one of three categories, Marketing, Utility or Authentication, so approve early. Pricing is per delivered message (conversation pricing ended July 1, 2025), so a fan-out loop costs per item.
#### Israeli SMS Gateways
| Gateway | Host | Auth | Best For |
|---------|------|------|----------|
| 019 Telzar | `019sms.co.il` | Bearer token, or username + password | Bulk marketing, transactional |
| InforUMobile | `capi.inforu.co.il` | Bearer token (IP allowlist enforced) | OTP, transactional |
| Nexmo/Vonage IL | `rest.nexmo.com` | API key + secret | International + local |
**019 returns HTTP 200 even when the send fails.** An auth failure comes back as `200 {"status":3,"message":"Username or password is incorrect..."}`, so a default HTTP Request node treats it as success and the workflow continues with no SMS delivered. Enable **Always Output Data** and branch on `$json.status` (0 = sent) with an IF node, never on the HTTP status code. InforUMobile additionally enforces an IP allowlist on top of the token, so whitelist your n8n egress IP there as well as at Cardcom and Tranzila.
Phone numbers must be international format `972XXXXXXXXX` (drop the leading 0), and normalization must VALIDATE rather than just reformat: an Israeli landline prefix (02/03/04/08/09, 072-077) and a 1-800 or *NNNN number cannot receive SMS at all, so a blind `0` to `972` swap silently mangles them. Request bodies, per-gateway field names and a normalizer that checks the prefix are in `references/israeli-api-endpoints.md`.
#### Three Israeli Rules That Gate the Whole Workflow
**1. Not every business may issue a tax invoice.** An עוסק פטור may not issue a חשבונית מס (305 / 320) and may not charge VAT at all; they issue a קבלה or חשבונית עסקה. Ask the VAT status before choosing a document type and never hardcode 305/320. Zero-rated cases (exports, services to a foreign resident) and the Eilat exemption also move the rate, so `vat_type` is a per-transaction decision. Numbering is sequential and immutable, a mistaken invoice is cancelled with a credit note (330) and never deleted, and the document is kept seven years, so persist the PDF rather than an expiring link.
**2. Automated commercial messaging is regulated.** s.30א of the Communications Law (תיקון 40) governs every SMS and WhatsApp send this skill can build. s.30א(ב) requires **explicit prior written consent** before you send at all. s.30א(י)(1) allows damages with no proof of damage of up to **1,000 NIS per message received in breach**, which is exactly the exposure of a bulk loop. Meta's 24-hour window is a platform rule, not the legal one, and clearing it does not clear consent.
The labelling requirement is channel-specific and routinely got wrong. The general rule, s.30א(ה)(1)(א), requires the word `פרסומת` at the START of the message. But s.30א(ה)(2) is an SMS carve-out: an advertisement sent by SMS states **only** the advertiser's name and the contact route for a refusal notice ("יציין בדבר הפרסומת רק את שמו ואת דרכי יצירת הקשר עמו לצורך מתן הודעת סירוב"). Do not bolt a `פרסומת` prefix onto an SMS template on the assumption that the general rule applies.
**3. The data these workflows touch is regulated personal data.** Teudat zehut from a payment callback, live bank credentials and transaction descriptions piped into a third-party LLM all fall under the Privacy Protection Law as amended (Amendment 13, in force August 2025). Minimise what you persist, treat the Google Sheet as a regulated database with a retention rule, and do not send identifiable customer data to a foreign model without a lawful basis.
### Step 3: Handle Hebrew Data in n8n Nodes
Code nodes process strings as UTF-8, so Hebrew works natively. Problems arise at boundaries: CSV exports need UTF-8-BOM or Excel reads them as ANSI, HTTP Request responses need an explicit UTF-8 response encoding, email bodies need `<div dir="rtl">`, data.gov.il returns Hebrew JSON keys that should be normalized in a Code node, and string length must use `Array.from(str).length` rather than `.length`. Legacy Israeli bank and accounting exports are often Windows-1255 rather than UTF-8, which BOM advice does not fix, and customer-supplied names can carry bidi control characters that visually reorder an amount on a rendered invoice.
Format currency with `Intl.NumberFormat('he-IL', { style: 'currency', currency: 'ILS' })`. Israeli documents use DD/MM/YYYY: Morning returns ISO 8601, but government datasets return DD/MM/YYYY strings, so parse explicitly rather than trusting `new Date(s)`. Parsing helpers, a Hebrew month map and a prefix-validating phone normalizer are in `references/israeli-api-endpoints.md`.
### Step 4: Shabbat-Aware Scheduling
Business workflows in Israel must not run during Shabbat (Friday sundown to Saturday sundown) and Jewish holidays. n8n's Schedule Trigger has no native support, so add a check node at the start of every scheduled workflow.
**Architecture:** Schedule Trigger -> HTTP Request (Hebcal) -> IF (is Shabbat?) -> Continue or Stop
```
GET https://www.hebcal.com/shabbat?cfg=json&geonameid=293397&M=on
```
`geonameid=293397` is Tel Aviv; Jerusalem is 281184, Haifa 294801, Beer Sheva 295530. Candle lighting is 40 minutes before sunset in Jerusalem, 30 in Haifa and Zikhron Ya'akov, 18 elsewhere, so the geonameid is load-bearing. Code node to gate the workflow:
```javascript
const now = new Date();
const items = $input.first().json?.items;
// FAIL CLOSED: if we cannot read the calendar we do not know, so we do not run.
if (!Array.isArray(items) || items.length === 0) return [];
// Pair each candle-lighting with the FIRST havdalah after it. Taking
// items.find('candles') and items.find('havdalah') is wrong: asked ON a chag,
// Hebcal returns that chag's havdalah BEFORE the next festival's candles, so
// the range test is unsatisfiable and the workflow runs on Yom Kippur.
const ev = items.filter(i => i.category === 'candles' || i.category === 'havdalah')
.map(i => ({ cat: i.category, at: new Date(i.date) }))
.sort((a, b) => a.at - b.at);
for (let k = 0; k < ev.length - 1; k++) {
if (ev[k].cat === 'candles' && ev[k + 1].cat === 'havdalah'
&& now >= ev[k].at && now <= ev[k + 1].at) return [];
}
if (ev.length && ev[0].cat === 'havdalah' && now <= ev[0].at) return [];
return $input.all();
```
**Do not silently drop the work.** `return []` ends the branch, so an invoice or customer notification due during Shabbat is lost rather than delayed. For anything a customer is waiting on, queue the items (workflow static data or a table) and drain them from a separate post-havdalah workflow.
For Jewish holidays, query `https://www.hebcal.com/hebcal?v=1&cfg=json&year=now&month=x&maj=on&mod=on&i=on` and filter for `yomtov: true`. Both query parameters are load-bearing and both fail silently. `month=now` is not valid: it returns HTTP 200 with `"items": []`, so the gate never fires. Without `i=on` Hebcal serves the Diaspora calendar, 13 yomtov days for 2026 against Israel's 8, halting the business on five ordinary working days. A weekday-only cron does NOT remove the need for this gate: six of Israel's eight 2026 yomtov days fall Sunday to Thursday, Yom Kippur among them. `references/shabbat-cron-patterns.md` carries the corrected patterns, the Israel-local date handling erev chag needs, and the fast-day and chol-hamoed cases this gate ignores.
### Step 5: Israeli Payment Gateway Webhooks
Three gateways cover almost all Israeli card traffic, each delivering its callback differently. Full field tables, base paths and the Bit flow are in `references/israeli-api-endpoints.md`; what decides correctness is here.
| Gateway | Callback shape | Success test | Dedup key |
|---|---|---|---|
| Cardcom v11 | POST `LowProfileResult` to your `WebHookUrl` | **`ResponseCode == 0`** | `TranzactionId` |
| Tranzila | GET query parameters | `Response == '000'` | `index` |
| Grow by Meshulam | POST `multipart/form-data` (not JSON) | `webhookKey` matches your stored key, then re-read | `asmachta` |
**`ReturnValue` is not a Cardcom status field.** Cardcom's own v11 spec calls it "A string of data to save on the transaction, usually send your unique order Id, you will get it back in the WebHook URL". It is your pass-through, echoed back. Branching on it approves declined payments. There is no `DealResponse` field in v11 at all, and `InternalDealNumber` / `CardOwnerID` / `NumOfPayments` are legacy classic-API names absent from the result object, so a dedup key built on `InternalDealNumber` is always `undefined` and the duplicate-invoice guard never fires. The teudat zehut and installment count live under `TranzactionInfo` as `CardOwnerIdentityNumber` and `NumberOfPayments`.
**Never issue a document straight off the callback body.** The webhook URL is public and none of these three gateways signs its payload, so a forged POST with a plausible amount makes your workflow issue a real tax invoice and burn a real allocation number. Re-read the transaction server-side first (Cardcom by `TranzactionId`, Grow via `getPaymentProcessInfo`, Tranzila by `index`), confirm the amount, then issue. Grow additionally needs `approveTransaction` to finalize.
Bit is not a standalone API: it is reached through Tranzila v2 (`bit: true` on the payment page) or through Grow with Bit enabled, arriving in the same webhook flow with a different `transactionType`.
#### Webhook Authentication
n8n's Webhook node supports four auth modes: None, Basic Auth, Header Auth, JWT Auth. Given the 2026 unauthenticated-webhook CVE history, "None" on a publicly-routable webhook is effectively a vulnerability. But for the three Israeli gateways above, transport auth is not the real control: none of them signs its payload, so the server-side re-read is what actually protects you. n8n's JWT Auth does verify the signature and the `exp` claim, but not `iss` or `aud`. `references/webhook-auth-patterns.md` has the working snippets and the raw-body caveat that breaks naive HMAC checks.
**IP whitelisting:** Cardcom and Tranzila require your webhook IP to be whitelisted. Self-hosting means a static IP or a fixed-egress reverse proxy.
### Step 6: Self-Hosting Considerations
#### n8n 2.x Security Line and Version Pinning
n8n 2.0 shipped in December 2025; current stable is 2.38.7 as of September 2026. **Pin >= 2.32.1** and never `n8nio/n8n:latest`. Three CRITICAL vulnerabilities (CVE-2026-44789 HTTP Request node prototype pollution to RCE, CVE-2026-44790 Git node arbitrary file read, CVE-2026-44791 XML node patch bypass) were disclosed 2026-05-14 and fixed in 2.22.1; HIGH-severity credential-exfiltration and sandbox-escape fixes landed through 2.31.5 and 2.32.1. CVE-2026-44789 sits on this skill's critical path, since every Israeli integration here is an HTTP Request node, and every payment-gateway workflow adds a public Webhook node.
Two settings from the 2.0 breaking changes gate this skill's code, and with task runners on (the 2.0 default) both belong on the **runner**, not the main container:
```
NODE_FUNCTION_ALLOW_BUILTIN=crypto
NODE_FUNCTION_ALLOW_EXTERNAL=israeli-bank-scrapers
```
`N8N_BLOCK_ENV_ACCESS_IN_NODE` also defaults to `true` in 2.x, so `$env.*` inside a Code node silently returns nothing. The credential store is NOT the answer here, because the Code node declares no credentials; pick one of the three routes in Step 2.
**n8n 3.0 lands October 2026** and removes self-hosted npm installs (Docker only), the Function / Function Item / Item Lists nodes, the `$getPairedItem` helper, and AI Agent node v1 with all its legacy agent modes. Build Step 7 on the current Tools Agent.
See `references/n8n-version-migration.md` for the full CVE history, the 2.0 breaking-change table, the Execute Command re-enable path, the complete 3.0 list, and Israeli data-residency hosting options.
#### Self-Hosting Settings That Matter in Israel
Set **both** `GENERIC_TIMEZONE=Asia/Jerusalem` and `TZ=Asia/Jerusalem`. Without them Schedule Trigger nodes run on UTC and Shabbat calculations drift 2-3 hours; Israeli DST runs from the Friday before the last Sunday of March to the last Sunday of October. Set a stable `N8N_ENCRYPTION_KEY` so the credential store survives restarts, pin the image tag, and put the Code-node module variables on the task-runner service. A ready Compose file and the Israeli data-residency hosting table are in `references/n8n-version-migration.md`.
### Step 7: n8n AI Agent Nodes for Israeli Workflows
n8n 2.x ships native LangChain integration (the "Advanced AI" node group): Tools Agent, memory nodes, Vector Store nodes (Pinecone, Qdrant, Supabase pgvector) for RAG, and Model nodes for OpenAI, Anthropic and local models via Ollama. Build on the **Tools Agent**: n8n 3.0 removes AI Agent node v1 along with the SQL, Conversational, OpenAI Functions, Plan-and-Execute and ReAct modes.
**Do not hardcode a model name from a tutorial.** The current Anthropic Chat Model node resolves its model list from the Anthropic API at runtime rather than from a fixed dropdown, so the list moves without an n8n release. Choose by requirement (a mid-tier frontier model for Hebrew classification, the strongest available for long Hebrew legal text, the lowest-latency in the family for real-time chat, a local multilingual model on an Israeli VPS when PII must stay in Israel), then take what the node offers today. For RAG over Hebrew corpora use a multilingual embedding model (Cohere `embed-multilingual-v3.0` or OpenAI `text-embedding-3-large`); `text-embedding-ada-002` is weak on Hebrew. Note that piping customer transaction descriptions into a foreign model is a privacy decision, not just a quality one.
**n8n MCP nodes.** The MCP Client Tool (`@n8n/n8n-nodes-langchain.mcpClientTool`) attaches as a sub-node so an AI Agent can call tools on an external MCP server, for example agentskills.co.il's `hebcal`, `israeli-bank` and `data-gov-il` servers. The MCP Server Trigger (`@n8n/n8n-nodes-langchain.mcpTrigger`) exposes an n8n workflow itself as an MCP tool for Claude Desktop, Cursor or Windsurf to invoke; from typeVersion 2 it also offers n8n User Auth (OAuth2). There is no `toolMcp` node: that name is a common hallucination and n8n rejects a workflow JSON using it. A worked Hebrew transaction-categorizer example is in `references/n8n-workflow-authoring.md`.
### Step 8: Platform Choice, Workflow JSON, and Credentials
Choose n8n over Make.com or Zapier when you need self-hosting for Israeli data residency, unlimited automations, or full code access for Israeli API quirks. None of the three ships built-in Israeli API nodes, so the work is HTTP Request and Code nodes either way; n8n is the only one you can host in an Israeli region.
Workflows are JSON: a `nodes` array (each with a unique `name` used as the connection key, a `type` such as `n8n-nodes-base.httpRequest`, a `typeVersion`, `parameters` and `position`) and a `connections` object keyed by source node name. Import via `POST /api/v1/workflows`, then **publish** before it runs. Pin the CURRENT `typeVersion` for each node rather than copying an old number from a tutorial; the Schedule Trigger, for example, is on 1.4 while most samples still show 1.2.
Secrets live in n8n's encrypted credential store, never inline in workflow JSON, with one exception that matters here: the Code node has no credentials, so bank and HMAC secrets need one of the routes described in Step 2. Morning has no native credential either, so chain an HTTP Request to `/account/token` and pass `Authorization: Bearer {{token}}` onward, refreshing per execution since the JWT expires after 60 minutes.
`references/n8n-workflow-authoring.md` has the full comparison table, a complete workflow JSON sample, and the per-service credential setup.
## Examples
### Example 1: Morning invoice reconciliation, weekday mornings
"Every morning, pull yesterday's Morning invoices and flag any still unpaid."
Schedule Trigger on `0 9 * * 0-4` (09:00 Israel time, since `GENERIC_TIMEZONE` is set) into the Step 4 holiday gate, since six of 2026's eight yomtov days fall inside that cron. Then HTTP Request to `/api/v1/account/token` for the JWT, HTTP Request to `/api/v1/documents/search` with `Authorization: Bearer {{$json.token}}` filtering `fromDate`/`toDate` to yesterday and `type` to 305/320, an IF node branching on payment status, and an SMS or Send Email node with the Hebrew body wrapped in `<div dir="rtl">`.
### Example 2: Bank transactions to a Google Sheet, holiday-aware
"Scrape my business account nightly and append new transactions to a sheet, but skip Shabbat and holidays."
Schedule Trigger, then the Hebcal request and the paired-scan gate from Step 4 (fail closed), then a Code node running `israeli-bank-scrapers` via `createScraper()` with a real secret route, then a Code node normalizing Hebrew descriptions and DD/MM/YYYY dates, then Google Sheets Append. Add a separate Error Trigger workflow, because an unattended scrape that throws simply stops with no notification.
## Recommended MCP Servers
- **hebcal**: Hebrew/Jewish calendar and Shabbat times, alternative to calling Hebcal HTTP in every workflow.
- **israeli-bank**: Israeli bank account data; lets an agent pull transactions without running `israeli-bank-scrapers` in a Code node.
- **data-gov-il**: Israeli government open data (CKAN), query registries without hand-building HTTP Request nodes.
## Reference Links
| Source | URL |
|--------|-----|
| n8n Documentation | https://docs.n8n.io/ |
| n8n 2.0 Breaking Changes | https://docs.n8n.io/changelog/v20-breaking-changes |
| n8n Enable Modules in Code Node | https://docs.n8n.io/deploy/host-n8n/configure-n8n/basic-configuration/configuration-examples/enable-modules-in-code-node |
| n8n Block Access to Nodes | https://docs.n8n.io/hosting/securing/blocking-nodes/ |
| Morning (Green Invoice) API | https://www.greeninvoice.co.il/api-docs |
| Hebcal API | https://www.hebcal.com/home/developer-apis |
| data.gov.il CKAN API | https://data.gov.il/api/3/action/help_show?name=datastore_search_sql |
## Gotchas
- **Agents pin `:latest` or a stale 2.1x/2.2x tag.** 2.21.4 alone carries 68 published advisories, three of them CRITICAL (the CVE-2026-44789/44790/44791 chain, fixed in 2.22.1), plus HIGH-severity credential-exfiltration fixes through 2.32.1. Any public Webhook node widens the exposure. Pin >= 2.32.1; current stable is 2.38.7.
- **Agents write `@n8n/n8n-nodes-langchain.toolMcp` for the MCP Client Tool.** No such node exists. The types are `mcpClientTool` and `mcpTrigger`.
- **Agents send a free-form WhatsApp message from a scheduled workflow.** Meta's 24-hour customer service window has closed by then, so only an approved template delivers. Use Send Template, and get the template approved before the workflow ships.
- **Agents retry an EZCount 417 as if it were a transient error.** 417 means the Tax Authority has not allocated a number; retrying never clears it. There is no `allocation_status` field. Branch on the 417 and surface the four documented options.
- **Agents read secrets with `$env` inside Code nodes.** Blocked by default in 2.x, so the read silently yields nothing and the node fails on an undefined value. But the credential store is not the fix (the Code node has none); pick one of the three routes in Step 2.
- **Agents call `require()` in a Code node without allowlisting the module.** `require('crypto')` needs `NODE_FUNCTION_ALLOW_BUILTIN=crypto`; `require('israeli-bank-scrapers')` needs `NODE_FUNCTION_ALLOW_EXTERNAL=israeli-bank-scrapers`. With task runners on (the 2.0 default) both go on the runner, not the main container.
- **Agents default to UTC for schedule triggers.** Israel uses `Asia/Jerusalem`; DST runs from the Friday before the last Sunday of March to the last Sunday of October. Set `GENERIC_TIMEZONE` and re-verify after each DST change.
- **Agents assume VAT is included.** Israeli invoices often show amounts before VAT (lifnei maam). Morning returns both `amount` (before VAT) and `totalAmount` (with VAT). Current VAT is 18% (2026).
- **Agents hardcode one candle-lighting time.** It varies by city (Jerusalem 40 min before sunset, Haifa and Zikhron Ya'akov 30, Tel Aviv and elsewhere 18), so a fixed time runs during Shabbat somewhere.
- **Agents branch a Cardcom webhook on `ReturnValue`.** It is your own order id echoed back, not a status. Use `ResponseCode == 0`, and dedup on `TranzactionId`, not the non-existent `InternalDealNumber`.
- **Agents build the Shabbat gate with `items.find('candles')` and `items.find('havdalah')`.** Asked on a chag, Hebcal returns that chag's havdalah BEFORE the next festival's candles, so the range test can never be true and the workflow runs on Yom Kippur. Pair each candles with the next havdalah, and fail closed when the calendar is unreadable.
- **Agents tell the user to put Code-node secrets in the credential store.** The Code node has no credentials. Unblock `$env` on the runner, or pass the secret in from a preceding node.
- **Agents issue an invoice directly from a payment webhook body.** The URL is public and none of the Israeli gateways signs its payload. Re-read the transaction server-side first.
- **Agents hardcode document type 305/320.** An osek patur may not issue a tax invoice or charge VAT at all. Ask the VAT status first.
- **Agents build an SMS or WhatsApp fan-out with no consent check or opt-out.** תיקון 40 לחוק התקשורת carries statutory damages up to 1,000 NIS per message with no proof of damage.
- **Invoice Reform 2026 threshold drops June 1, 2026.** Invoices over the threshold (10K NIS through May 31, 5K NIS from June 1) created via API require allocation numbers. Measured **before VAT**, so compare against Morning's `amount`, not `totalAmount`. Make it a workflow variable. The statute says `עולה על` (exceeds), so use `>`, not `>=`, and never gate on whether the buyer asked: s.38(א1) blocks the buyer's deduction regardless.
- **Agents build the Hebcal holiday gate with `month=now` and no `i=on`.** `month=now` returns an empty `items` array with HTTP 200, so the gate never fires; omitting `i=on` returns the Diaspora calendar (13 yomtov days for 2026 against Israel's 8). Use `month=x&i=on`.
- **Agents trust the HTTP status from Israeli SMS gateways.** 019 returns HTTP 200 with `status: 3` on auth failure. Branch on the body field, not the status code.
- **Agents assume one webhook equals one payment.** All three gateways retry. Deduplicate on `TranzactionId` / `index` / `asmachta` before creating a document, or a retry issues a duplicate invoice and burns a second allocation number.
- **n8n editor keyboard shortcuts break under Hebrew layout.** The canvas reads `e.key` rather than `e.code`, so `Ctrl+C` yields `e.key = 'ב'`. Switch to English input while editing. n8n issue #12569.
- **n8n's expression editor has no RTL support.** Hebrew renders left-to-right. Store long Hebrew literals in static workflow data and reference them by name.
- **Unattended workflows fail silently without an Error Trigger.** A scheduled scrape that throws just stops. Add a separate Error Trigger workflow that alerts. Enable per-node Retry On Fail for transient failures, but never on the Shabbat gate: a retry that eventually returns empty data must fail closed.
## Bundled Resources
### References
- `references/israeli-api-endpoints.md` -- Israeli API endpoint reference (Morning, data.gov.il, SMS gateways, payment gateways, Hebcal).
- `references/shabbat-cron-patterns.md` -- Pre-built Shabbat-aware scheduling patterns with Hebcal integration.
- `references/webhook-auth-patterns.md` -- HMAC signature verification + JWT claim validation Code-node snippets.
- `references/n8n-version-migration.md` -- full CVE history, the 2.0 breaking-change table, the complete 3.0 removal list, Israeli hosting options.
- `references/n8n-workflow-authoring.md` -- platform choice, workflow JSON shape, credential setup, AI-agent detail, extended troubleshooting.
## Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Morning API returns 401 | JWT expired (60 min TTL) | Refresh the token at the start of every execution; cache it in `$getWorkflowStaticData('global')` with a timestamp and refresh past 55 min |
| Hebrew garbled in CSV export | Missing UTF-8 BOM, so Excel reads ANSI | Set Spreadsheet File encoding to UTF-8-BOM |
| Cardcom callbacks never arrive | URL not publicly reachable, or IP not whitelisted | Public HTTPS with valid SSL, `WEBHOOK_URL` matching the public host, n8n's IP whitelisted in the Cardcom dashboard |
| Schedule Trigger runs during Shabbat | Server timezone is UTC, or the gate paired the wrong candles/havdalah (see Step 4) | Set `GENERIC_TIMEZONE` and `TZ`, then log `new Date().toString()` in a Code node to confirm; use the paired-scan gate |
| israeli-bank-scrapers fails in a Code node | `require()` blocked, secrets undefined, or wrong per-bank credential keys | Set `NODE_FUNCTION_ALLOW_EXTERNAL` **on the task runner**, pick a working secret route (Step 2), and read `SCRAPERS[companyId].loginFields` |
| Cloudflare blocks Amex/Isracard scraping | Bot detection on headless browsers | Use the maintained `@sergienko4/israeli-bank-scrapers` fork (Camoufox) |
Longer diagnoses, including the runner-image dependencies and memory floor for Chromium, are in `references/n8n-workflow-authoring.md`.