omnifox

n8n community nodes to interact with the Omnifox omnichannel platform REST API.

Package Information

Released: 1 mo ago
Downloads: 0 weekly / 0 monthly
Latest Version: 0.9.0
Author: Omnifox

Documentation

n8n-nodes-omnifox

This is an n8n community node package that lets you use Omnifox in your n8n workflows.

Omnifox is an omnichannel messaging platform that unifies WhatsApp, SMS, email, voice, and webchat into a single inbox and API.

n8n is a fair-code licensed workflow automation platform.

Installation
Description
Features
Credentials
Compatibility
Resources
Version history

Description

n8n-nodes-omnifox exposes the Omnifox REST API as native n8n nodes. It ships with:

  • An Omnifox action node covering 127 operations across 21 resources: the inbox (conversations, messages, contacts, tags, channels, campaigns), the CRM (companies, deals, pipelines, activities), Boards, the Calendar, the product catalog and quotes, reports, Omnifox's own workflow engine — plus a Custom API Call operation for any endpoint not modelled explicitly.
  • An Omnifox Trigger node that subscribes to Omnifox webhooks so workflows fire on incoming messages, status updates, and other platform events.

Features

  • Native n8n node with resource/operation dropdowns for the Omnifox REST API.
  • Resource Locators and dependent dropdowns: pick records and their parents from searchable lists instead of typing IDs.
  • usableAsTool, so an n8n AI Agent can drive Omnifox on its own.
  • Automatic pagination across the four response envelopes the API uses, and binary in/out for media uploads and quote PDFs.
  • API errors unpacked into actionable messages (validation fields, plan gates, missing token abilities).
  • Webhook-based trigger with automatic subscription lifecycle (create / checkExists / delete).
  • API token + workspace ID authentication via a reusable credential.
  • Configurable base URL so the same package works against staging and production Omnifox environments.

Installation

Follow the installation guide in the n8n community nodes documentation.

Via the n8n UI (recommended)

  1. Go to Settings > Community Nodes in your n8n instance.
  2. Click Install.
  3. Enter n8n-nodes-omnifox as the npm package name.
  4. Agree to the risks and click Install.

Via npm (self-hosted)

npm install n8n-nodes-omnifox

Then restart your n8n process so the new nodes are picked up.

From a local tarball (development builds)

Build the package and install the tarball into n8n's custom-nodes directory:

npm install && npm run build && npm pack     # -> n8n-nodes-omnifox-0.6.0.tgz

mkdir -p ~/.n8n/nodes
cd ~/.n8n/nodes
npm install /path/to/n8n-nodes-omnifox-0.6.0.tgz

Restart n8n (systemctl restart n8n, docker restart n8n, ...). "Omnifox" and
"Omnifox Trigger" then appear in the node palette.

For Docker, mount or copy the tarball into the container first and make sure
~/.n8n is the persisted volume (/home/node/.n8n in the official image).

Credentials

The package ships a single credential type: Omnifox API.

You will need:

  • An API token issued from the API keys screen of your Omnifox account.
  • The base URL of the Omnifox instance (defaults to https://omnifox.io).
  • Optionally a workspace ID — leave it at 0 unless your token is not
    pinned to a workspace. Tokens created in the API keys screen already carry
    theirs, and giving a different one is refused with a 403.

The credential authenticates outgoing requests with a Bearer token header.

Compatibility

  • Requires n8n >= 1.0.0.
  • Tested against the current n8n-workflow peer dependency.

If you run into a compatibility issue with a specific n8n version, please open an issue.

Development

npm install
npm run lint     # n8n-nodes-base ruleset, the same one the verification uses
npm run build    # tsc + copy icons, codex files and translations into dist/
npm test         # dry-runs every resource/operation against a stubbed HTTP layer

npm test walks all 21 resources, executes each of their operations with sample
parameters and asserts that every one issues the request it should. It is what
catches an operation that exists in the UI dropdown but was never wired into
execute() — the failure mode that silently returns an empty object. It runs
automatically on npm publish via prepublishOnly.

End-to-end against a real workspace

test/e2e.js drives the compiled node through a real IExecuteFunctions and a
real credential, so it verifies the contract rather than the wiring: routes,
payload shapes, response envelopes and the pagination walker as the server
actually implements them.

# One-off: seed the two fixtures the REST API cannot create (an inert workflow
# and a draft broadcast). Run from the Laravel root of the target instance.
sudo -u www-data php fixtures.php <organization-id> [workspace-id]

OMNIFOX_TOKEN=... OMNIFOX_BASE_URL=https://app.omnifox.io OMNIFOX_WORKSPACE_ID=1 \
OMNIFOX_E2E_WORKFLOW_ID=... OMNIFOX_E2E_BROADCAST_ID=... \
node test/e2e.js --write

Without --write only read operations run. With it, the mutating flows run too
and delete every record they create. The run finishes with a coverage line
naming any declared operation it could not exercise.

A call whose failure is the correct server answer for the state the harness
can produce (retrying a message that never failed, looking up a phone with no
conversation) is recorded as "refused as expected" — but only when the status
code matches, so a different error still fails the run.

Resources

Version history

0.6.6

Conversation → Send Template stopped being a guessing game. Until now the
only way to fill a template was a text box labelled Body Variables where you
typed Ana,12345 — with no way to see how many variables the template takes,
what they mean, or what the message actually says.

The dropdown previews the template. Every entry now shows its category,
language, variable count and the body text underneath the name, so you read the
message before committing to it.

One input per variable. Picking a template loads its {{N}} placeholders as
real fields, each labelled with the fragment of the template it lands in —
{{2}} — …tu pedido {{2}} ya salió…. They are required, so a missing value is
flagged in the panel instead of coming back as a 422 at run time. A template
with no variables says so and shows its text.

Dynamic buttons finally work. Templates whose button carries a {{1}} in
the URL, in the quick-reply text or in a copy-code button could not be sent at
all: the node never built the button_params the API expects, so Meta rejected
them. Each one now gets its own input, labelled with the URL or text it fills.
Phone-number buttons are skipped — Meta has no parameter slot for them.

Body Variables (Fallback) keeps the old comma-separated behaviour and is used
only when the mapper is empty, which is what happens when the template ID itself
comes from an expression and n8n cannot know which template to load.

Still not supported: carousel templates need a per-card payload the node
doesn't build. The variable list flags them rather than letting the send 422.

0.6.5

Three fixes and one gap, all found by using the node for real.

The WhatsApp template list was empty for everybody. Conversation → Send Template loads its dropdown from the conversation, and since 0.6.0 that field
is a Resource Locator — an object, not a number. The loader read it raw, got
NaN, and silently returned an empty list. Same bug in the board dropdown of
Create Board Card. Both now unwrap it.

On top of that the list works even when the conversation comes from an
expression, which is the normal case in a workflow: n8n cannot resolve one
while the parameter panel is open, so the node now falls back to every approved
template of every WhatsApp channel in the workspace (templates belong to the
channel, not to the conversation). The channel name is appended when there is
more than one number.

Conversation → Assign can now override how a team spreads the work. New
Distribution Override collection, shown only for Team: distribution
(round-robin / least load / first available / shared), only-online members, and
a max-open cap. It applies to that call alone — nothing is written to the team.
Without it the team's own rule applies, exactly as before.

Worth knowing: handing a conversation to a team has always honoured
teams.assignment_strategy, whose default is shared — no owner, straight to
the team's shared inbox. If a workflow "assigns to Sales" and nobody seems to
pick it up, that is why.

Team → Update. The team resource only had List, so the persistent rule
could not be changed from a flow at all. Name, distribution, only-online,
max-open and active are now editable.

Operations: 125 → 127.

0.6.4

Knows whether WhatsApp still lets you write. New operation
Conversation → Get Sending Window, wrapping
GET /conversations/{id}/window-status.

WhatsApp (and TikTok) only allow free-form text for 24h after the customer's
last message; after that only an approved template goes through, and a workflow
that keeps calling Message → Send just collects errors. The operation answers

{ "open": false, "channel_type": "whatsapp",
  "expired_at": "2026-07-26T18:20:11+00:00",
  "reason": "More than 24 hours since the last reply from the contact. Only approved templates can be sent." }

so an IF node can route between Message → Send and
Conversation → Send Template. open is always true on channels with no
window (webchat, email, Telegram…), so the same branch works everywhere. On a
contact who has never written, open is false with expired_at: null — on
WhatsApp you open with a template, on TikTok you cannot write at all until they
message first.

Worth knowing: the window belongs to the contact + channel pair, not to the
conversation. Closing and reopening a thread does not reset it, and a brand-new
conversation with a contact who wrote 10 minutes ago is already open.

Operations: 124 → 125.

0.6.3

Fixes a credential default that broke every call. Workspace ID was a
required field defaulting to 0, and the credential sent it on every request.
Tokens issued from the API keys screen are pinned to a workspace, and the
server refuses a mismatch — so a fresh install answered
403 API token is scoped to workspace 1 and cannot operate on workspace 0
until you happened to type the right number.

Workspace ID is now optional. Leave it at 0 and the workspace is read from
the token, which is what almost everybody wants. Set it only for a token that
is not pinned to one (those come from POST /auth/login), and the node adds
workspace_id to the request just for those.

0.6.2

Three more trigger events, now that the server accepts them.

conversation.client_no_reply_inactive, conversation.agent_no_reply_inactive
and payment.succeeded were always emitted by Omnifox but rejected by the
subscription whitelist, so no client could ever receive them. This package
reported the gap; the fix shipped on 2026-07-26 and the three are offered here
from this release. Against an older Omnifox, activating a workflow that uses
them answers 422.

Trigger events: 26 → 29.

0.6.1

No behaviour change — this release records that the node has now been run
against a live Omnifox workspace
, not just verified by reading the code.

All 124 declared operations were exercised end to end (141 calls, 171 HTTP
requests) against a production test workspace: 138 succeeded and 3 were refused
by the server for the right reason (retrying a message that never failed,
looking up a phone with no conversation, and sending a WhatsApp template on a
workspace with no WhatsApp channel — the controller's own guard). Zero
unexpected failures. The read:crm-only token path was checked too: writing to
/crm and reading /boards both 403 with the node's explanatory hint.

The run surfaced two real backend bugs in Omnifox, both since fixed:

  • POST /api/v1/quotes/{id}/send was a guaranteed 500. QuoteRoute is a
    central-connection model but was never pinned to it, so the write landed on
    tenant<org>.quote_routes — a table that does not exist. Now uses stancl's
    CentralConnection, like ChannelRoute / LeadFormRoute.
  • POST /api/v1/conversations/bulk/tag was a guaranteed 500 for every
    caller, the React inbox's bulk bar included. It inserted into a
    conversation_tag table that no migration has ever created; the real pivot is
    the polymorphic taggables.

Added test/e2e.js (the live harness) and test/fixtures.php (seeds the two
records the REST API cannot create). Neither ships in the package.

0.6.0

From 38 operations to 122, across 21 resources — the whole platform, plus a
raw escape hatch so nothing is out of reach.

New modules

  • CRM — Companies (CRUD + link/unlink contacts), Deals (CRUD, Kanban stage
    move, close won/lost with a reason from the catalog, bulk action over up to
    500 deals, timeline, per-pipeline Kanban board), Pipelines and Stages (full
    CRUD), Activities (calls, meetings, notes, emails, tasks; create against a
    contact / company / deal, complete, update, delete).
  • Boards ("Proyectos") — boards (CRUD, duplicate, full view, columns,
    activity log, groups) and items (create with initial cell values, update,
    move between groups, set a single cell value, duplicate, comment feed).
  • Calendar — calendars, service types, free-slot lookup, booking,
    rescheduling and cancellation.
  • Catalog — Products (CRUD) and Quotes (CRUD with a repeating line-item
    editor, send over email/WhatsApp with a dry-run mode, accept, reject, and
    PDF download as binary data).
  • Workflows — list, get, activate, deactivate and trigger Omnifox's own
    automation engine with a context payload.
  • Reports — overview, conversations, agent performance, message heatmap,
    plus the CRM funnel, forecast and win/loss-reasons reports, all with the
    preset date ranges the dashboards use.
  • Custom API Call — any endpoint under /api/v1, with the credential,
    pagination and error handling already applied. Coverage is no longer capped
    by what the node models explicitly.

Modernisation

  • Resource Locators for conversations, contacts, deals, companies, boards
    and products: pick the record from a searchable list, paste an ID, or use an
    expression. Contacts keep a third mode for the email: / phone: /
    external: identifiers the API accepts.
  • Dependent dropdowns — stages follow the selected pipeline, groups and
    columns follow the selected board.
  • Errors that say what to do. A 422 is unpacked into field: message
    lines; a 402 names the plan/wallet gate; a 403 on a /crm or /boards route
    explains that the token needs read:crm/write:crm (or …:projects) and
    that the workspace plan must have the module on; a 404 on a workspace-scoped
    route reminds you IDs do not cross workspaces.
  • Trigger: activation failures now explain the two real causes — the
    subscription endpoint requires a workspace admin, and Omnifox only accepts
    HTTPS webhook URLs (so WEBHOOK_URL on n8n has to be a public https
    address).
  • Spanish translations extended to every new resource, operation and field.

Breaking change. Conversation ID and Contact ID became Resource
Locators. Workflows built on 0.5.0 need those two fields re-picked once (or set
to "By ID" mode); everything else is unchanged.

Still not covered by a dedicated resource — reachable through Custom API
Call
: Co-browse, Voice/IVR, CSV import, saved inbox views, board automations
and forms.

0.5.0

From 8 operations to 38, across 9 resources, plus the plumbing that makes the
node behave like a first-party one.

Three more bugs found while reading the controllers. None of them could
have worked in 0.4.0:

  • Tag → Add to Conversation posted {ids: [...]} to /conversations/bulk/tag,
    but ConversationBulkController validates conversation_ids. Every call
    came back 422.
  • Media messages (image/video/audio/voice/file) were sent with file_url only.
    SendMessageRequest marks the multipart file part as required for those
    types, so a URL alone is rejected. Media now travels as a real upload, taken
    either from the incoming binary data or downloaded from a URL first.
  • Send → To Contact posted a flat {type, content_text} body.
    POST /contacts/{ID}/message expects the respond.io-shaped envelope
    {channelId, message: {type, ...}}. Rewritten to match, with its six real
    message types (text, attachment, quick reply, WhatsApp template, email,
    custom payload).
  • Send → Standalone was dropped: POST /messages requires a contact_id
    anyway, which makes it a strict subset of the contact mode. The node never
    sent that field, so the mode was dead on arrival.

New in the node itself:

  • Dynamic dropdowns for channels, teams, users, AI agents, tags, WhatsApp
    templates and boards — no more typing numeric IDs by hand.
  • usableAsTool, so an n8n AI Agent can call Omnifox on its own.
  • Codex files (*.node.json) giving both nodes their categories and the
    "Docs" link.
  • Return All with automatic pagination, handling all four envelopes the API
    uses (plain, paginated(), Laravel paginator, and cursor mode).
  • Spanish translations extended to the new surface.

New operations — Conversation: add note, add tag, assign, change status,
create board card, delete, find or create, get, list, list notes, list
templates, lookup, request follow-up, send template, snooze. Contact: add
comment, add tags, delete, find or create, get, list, list channels, merge,
remove tags, search, update. Message: get, list, retry, send. Tag: create,
list. Broadcast: cancel, get, list. Channel: get, list. Team / User / AI
Agent: list.

Still not covered: CRM (companies, deals, pipelines), Boards beyond creating a
card, Calendar and bookings, Workflows, Reports, Co-browse and Voice.

0.4.0

  • Spanish UI translations for both nodes and the credential, at
    nodes/<Node>/translations/es/<node>.json. They are picked up only when the
    n8n instance runs with N8N_DEFAULT_LOCALE=es — the locale is instance-wide,
    not per user, and it translates the whole editor, not just this node.
    The action labels shown in the node's Actions panel are not covered by
    n8n's node-translation keys and stay in English.
  • gulp build:assets (still aliased as build:icons) now copies the
    translation JSON into dist/ alongside the icons.

0.3.0

  • Node icons now use the actual Omnifox mascot (omnifox.png /
    omnifoxTrigger.png, from the brand asset branding/icon-128x128.png).
    0.2.0 shipped the PWA glyph from public/icons/, which is not the logo.

0.2.0

First build-verified release. Everything below was checked against the live
Omnifox codebase (routes/api.php, WebhookController, WebhookDispatcher,
BaseApiController) on 2026-07-25.

  • Fixed: the action node issued requests with a relative URL. Node
    execute() is programmatic, so requestDefaults.baseURL never applied and
    every call would have failed. The base URL is now read from the credential.
  • Fixed: the credential test hit GET /api/v1/me, which does not exist
    (/api/v1/me/* is a prefix group with no index route). It now uses
    GET /api/v1/auth/me.
  • Fixed: the workspace was sent as an X-Workspace-Id header that the API
    never reads. It now travels as the workspace_id query param, which is what
    the server-side resolver falls back to for tokens that are not workspace-bound.
  • Fixed: the package did not compile — NodeConnectionType is a type-only
    export in current n8n-workflow, and two NodeApiError call sites had the
    wrong cast. Added the missing @typescript-eslint/* devDependencies so
    npm run lint (and therefore prepublishOnly) can run at all.
  • Trigger events: 10 → 26. The list is now the verified intersection of
    WebhookController::ALLOWED_EVENTS and the events actually dispatched by
    WebhookDispatcher. Adds message delivered/read, conversation
    reopened/pending/snoozed/auto-closed/SLA-breached/agent-idle, contact
    deleted, note created, call started, agent assigned/status changed, tag
    added/removed and broadcast completed.
  • Trigger: the unused "Description" field (silently dropped by the API) was
    replaced with "Subscription Name", which maps to the required name field of
    POST /api/v1/webhooks.
  • Publish-ready: npm run lint (now covering package.json under the
    n8n-nodes-base/community ruleset) and npm publish --dry-run both pass.
    Added repository, bugs and engines; the placeholder "OM" icons were
    replaced with the real Omnifox logo.

Known gaps, unchanged in this release:

  • Tag → remove from conversation still has no Sanctum-authenticated endpoint
    on the Omnifox side (see docs/OPERATIONS.md §8).
  • Conversation → change status supports open / resolved / closed. There is no
    endpoint for pending or snoozed via a status setter.
  • Four events are accepted by the API but never emitted, so they are not
    offered: workflow.completed, subscription.created|updated|cancelled.
  • Three events are emitted but rejected by the subscription whitelist, so they
    cannot be subscribed to yet: conversation.client_no_reply_inactive,
    conversation.agent_no_reply_inactive, payment.succeeded.

0.1.0

  • Initial scaffold: package layout, credential stub, action node stub, trigger node stub.
  • Operations are TODO and will land in a follow-up release.

Discussion