Skip to content

n8n

This page builds one working automation end to end: an inbound WhatsApp message creates a task named Write back to <contact>, linked to that contact.

It uses n8n-nodes-alphone, the community node for AlphOne. For the engine neutral view, and for driving AlphOne from any other engine with plain HTTP, see the automation guide.

  • An n8n instance you administer, self hosted or cloud
  • AlphOne 0.7.0 or newer, reachable from n8n over HTTP
  • An AlphOne admin account, to mint the token the trigger uses

In n8n, open Settings then Community nodes then Install, and enter n8n-nodes-alphone. Only Owner and Admin accounts may install community nodes.

Two nodes appear afterwards. AlphOne performs actions, and AlphOne Trigger starts a workflow when something happens.

Self hosted instances need community packages enabled, which is the default. If installation is refused, set N8N_COMMUNITY_PACKAGES_ENABLED=true and, until the node passes n8n verification, N8N_UNVERIFIED_PACKAGES_ENABLED=true.

An engine cannot hold a browser session, so it authenticates with a token. On the AlphOne host:

Terminal window
alphone token:create -email you@example.com -name "n8n" \
-scope meta:read -scope webhooks:write -scope tasks:write -scope contacts:read

Those four cover this guide: meta:read for the credential test, webhooks:write for the trigger to register and remove its subscription, tasks:write to create tasks, and contacts:read to look contacts up. Drop any your workflow does not need.

The AlphOne Trigger node creates, lists and deletes its subscription with this token, and webhooks:write alone is not enough for that. The token’s account must also hold the manage_webhooks capability, which in a stock install means an admin, so mint the token for an admin’s account. A member’s token still works for the AlphOne action nodes, such as tasks and contacts. To keep those on a member’s token, create a second credential with an admin’s token and pick it on the AlphOne Trigger node only.

The secret prints once and is stored only as a hash. Copy it now, because it cannot be recovered, only replaced.

A token acts as the user who created it, so create it against the account whose work the automation should own. Disabling that user stops their tokens the way it already stops their sessions.

Two limits narrow it further. Each -scope names an area and whether the token may write there, and a token holds nothing it was not granted. Leave -scope out and the token gets every area, which is rarely what an automation needs. Area names are exact, so a typo like contact:read is refused when you mint it rather than failing later.

A token also never reaches further than the person who created it. Mint it against an account that already does the work you are automating.

A token also expires. Without -ttl it lasts ninety days by default, set with ALPHONE_TOKEN_TTL_DAYS, and after that every request answers invalid token. Pass -ttl 30 for a shorter life, or -ttl never for one that does not expire. Run alphone token:list -email you@example.com to see the scopes and the expiry date of every token you hold.

Open the Credentials tab and press Create credential.

The Personal page with the Credentials tab selected and the Create credential button highlighted.

Search for AlphOne API and select it.

The Add new credential dialog with AlphOne API typed in the search field and highlighted in the results.

Fill in two fields and save.

FieldValue
Base URLwhere n8n reaches AlphOne, with no trailing slash
API Tokenthe secret from step 2

The AlphOne account credential form showing the Base URL set to host.docker.internal and the API token masked.

The base URL depends on where each side runs:

AlphOne runsBase URL from inside n8n
On your machine, n8n in Dockerhttp://host.docker.internal:8080
Both in the same compose projecthttp://alphone:8080
A deployed instanceits public URL

localhost inside a container means the container itself, never your machine, so it will not reach AlphOne.

Press the test button. It asks AlphOne for its version, which needs a valid credential, so a success there proves the whole connection.

Open the Workflows tab and press Create workflow.

The Personal page with the Workflows tab selected and the Create workflow button highlighted.

Press Add first step and search for alphone.

The empty workflow canvas with the Add first step panel open and alphone typed in the search field.

The node offers a trigger for each event it supports.

The AlphOne node panel listing one trigger per event, among them on whats app message received.

Choose On whats app message received.

Pick the credential from step 3, and confirm the event reads WhatsApp Message Received.

The AlphOne Trigger node with its credential selected, the WhatsApp Message Received event chosen, and the Execute step button highlighted.

Press Execute step. n8n waits for a real event.

The trigger waiting, showing Listening for test event and an instruction to create an event in AlphOne.

While it listens, send a WhatsApp message to your connected number. The envelope arrives:

The trigger output showing one item with id, event, occurred at, and a data object holding contact id, contact name, conversation id, external id, and text.

Every delivery carries the same four fields. id names the event and stays the same across retries, so store it if you need to drop duplicates. data carries the subject, and for whatsapp.message.received that means conversation_id, contact_id, contact_name, external_id, and text. The webhooks reference documents the envelope in full.

Testing this way needs no published workflow, which makes it the fastest way to see real data before building the rest.

Press the + after the trigger. The node groups its actions by resource, one group per kind of record it reads and writes.

The workflow canvas with the trigger connected, and the node details panel listing the actions grouped by resource.

Choose Create a task and set:

FieldValue
ResourceTask
OperationCreate
TitleWrite back to {{ $json.data.contact_name }}
Due Onleave the default

Then Add Field under Additional Fields:

FieldValue
Contact ID{{ $json.data.contact_id }}

Switch each of those to expression mode with its fx toggle, then type the value without a leading =. n8n adds that prefix itself when it stores the parameter, so typing one leaves a stray = in the value. A stray = in Title is stored as part of the task title, and a stray = in Contact ID is refused with a VALIDATION error naming the bad uuid, which the node reports as a failed step. The small preview under each field shows the resolved value, so check it reads Write back to Maria Perez and a bare uuid before running.

Press Execute step.

The Create a task node with its title and contact id expressions resolved, and the output showing the created task with a Node executed successfully message.

The task exists in AlphOne now, linked to the contact who wrote in.

Name the workflow and press Publish.

The finished workflow with the trigger and the task node connected, and the Publish button highlighted.

Publishing is what registers the webhook. The trigger asks AlphOne to create a subscription and stores its id and its signing secret in workflow static data. An unpublished trigger has no subscription, so nothing is delivered to it.

Confirm AlphOne agrees, using the token from step 2:

Terminal window
curl -s https://your-domain/api/graphql \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"query":"{ webhooks { id url events } }"}'

A subscription pointing at your n8n webhook url means the loop is live. From now on, every inbound WhatsApp message creates its task on its own.

n8n builds its webhook urls from its WEBHOOK_URL setting, and AlphOne refuses to deliver to internal addresses unless the operator allows them. Which one you have decides whether AlphOne needs an entry:

n8n’s webhook urls start withAdd to AlphOne’s environment
a public https addressnothing
http://n8n:5678, both in the same compose projectALPHONE_WEBHOOK_ALLOWED_HOSTS=n8n:5678
http://localhost:5678, both on your machineALPHONE_WEBHOOK_ALLOWED_HOSTS=localhost:5678

Restart AlphOne after adding it. The entry opens that one host and port and nothing else on the network. Without it the workflow publishes and the subscription exists, but every delivery is refused. Configuration explains the format.

A task created with an API token records token:<name> in its origin_source, so work an automation created stays distinguishable from work a person typed. Attribution comes from the credential rather than the request, so a workflow cannot claim to be someone else, and it needs nothing from you.

AlphOne signs every delivery with HMAC-SHA256 over the exact bytes sent, and the node verifies that signature before running the workflow, so a forged request is rejected. Delivery is at least once, retried with a widening wait for 24 hours, so a workflow that survives a restart catches up on its own. Answer quickly, because AlphOne waits 10 seconds.

To see what happened from the AlphOne side, read the delivery queue:

Terminal window
docker compose exec postgres psql -U postgres -d alphone -c \
"SELECT event_name, status, attempts, last_error
FROM core.webhook_deliveries ORDER BY created_at DESC LIMIT 5"

delivered with attempts = 1 means the chain worked first time. A last_error naming webhook: address refused means AlphOne refused n8n’s address, see When n8n runs next to AlphOne.

SymptomCause
The credential test failsThe base URL has a trailing slash, or says localhost where the container cannot reach AlphOne, or the token expired
invalid tokenThe token expired, was revoked, or the token id was pasted instead of the secret. Run alphone token:list -email you@example.com to see its expiry. Only the value starting a1_ authenticates
scope required: contacts:writeThe token was not granted that area. Mint a replacement with the -scope it needs, tokens cannot be widened in place
Nothing arrives after publishingNo subscription exists. Ask for webhooks and republish
Publishing a workflow with an AlphOne Trigger fails with admin requiredThe credential’s token belongs to an account without manage_webhooks, which in a stock install means it is not an admin. Mint a token for an admin’s account and use it in the trigger’s credential
A subscription exists but nothing arrives, AlphOne logs refusing a webhook delivery to an internal addressn8n’s webhook url is on your own network. Add its host and port to ALPHONE_WEBHOOK_ALLOWED_HOSTS, see above
Publishing fails with url names an internal addressn8n’s webhook url is an internal IP address. Add that address and its port, such as 10.0.0.5:5678, to ALPHONE_WEBHOOK_ALLOWED_HOSTS, or set WEBHOOK_URL to a public https address. A host name in its place passes this check, but every delivery is then refused until its entry is listed
Deliveries stuck pending with subscriber answered 301 or another 3xxn8n’s webhook url forwards somewhere else and AlphOne never follows. Set WEBHOOK_URL to the final address
A task titled with literal {{ }}The field is not in expression mode. Use its fx toggle
The task node fails with a validation error naming the id, previews show a leading =An = was typed into the expression editor. n8n adds it, so delete yours
Deliveries stuck pending with subscriber answered 404A subscription outlived its workflow. Delete it with the deleteWebhook mutation

The same two nodes cover every event AlphOne publishes, listed in the webhooks reference, and every action the node offers. A schedule trigger with Get many tasks filtered by due_before makes a morning digest of overdue work, with no trigger registration involved.

The package ships example workflows in its examples folder. Import one from the n8n canvas menu, then open each AlphOne node and pick your credential, which the files leave as a placeholder.

  • overdue-task-digest.json collects the tasks you have not finished and builds a message from them, every morning at eight.
  • whatsapp-message-to-task.json creates a task the moment a WhatsApp message arrives, linked to the contact AlphOne resolved it to.
  • import-to-daily-tasks.json turns a finished contact import into a call list spread over days, twenty a day, with each task carrying its import row so a re-run creates nothing twice. The automation guide explains the recipe behind it.