Connect n8n to Atlas Labs
To connect n8n to Atlas Labs, use a Webhook node as the trigger and an HTTP Request node with a Bearer Auth or Header Auth credential for calls. A self-hosted n8n on its default port will be refused, because Atlas Labs only delivers to public HTTPS on port 443, so put it behind a tunnel or a reverse proxy first.
| At a glance | Value |
|---|---|
| Receive events | Webhook node, HTTP Method POST, use the Production URL; Options → Raw Body on for signature checks; Respond “Immediately” answers 200 before later nodes run, and what Atlas Labs sees when a later node fails under the other Respond modes is not something we have confirmed |
| Call the API | HTTP Request node; shortcut: Parameters → Import cURL with any curl snippet from these docs |
| Where the key goes | Authentication → Generic Credential Type → Bearer Auth (paste only lab_off_YOUR_KEY) or Header Auth (Name Authorization, Value Bearer lab_off_YOUR_KEY) |
| Body | Send Body → JSON; n8n sets Content-Type: application/json |
Idempotency-Key | Send Headers → Idempotency-Key = the id the Code node outputs after parsing the raw body, or the Webhook node's webhook-id header referenced by node name, $('Webhook').item.json.headers['webhook-id'] (check the node name in your workflow); both equal the event id. Not $json.headers[…]: in the HTTP Request node $json is the previous node's output, the GET-order response |
| Webhook host | n8n Cloud URLs on *.app.n8n.cloud are recognised; self-hosted needs public https:// on port 443 |
| Retries | Settings → Retry On Fail, Max Tries, Wait Between Tries (short); for 429 prefer a Wait node or HTTP Request Batching |
| OpenAPI import | None in the HTTP Request node; only Import cURL |
lab_off_YOUR_KEY and lab_fac_YOUR_KEY. After you start a sandbox, the “Connect it” panel's n8n tab shows the credential values and an Import cURL command with your real keys. to the Production URL
Bearer Auth credential
How do I receive Atlas Labs events in n8n?
- Add a trigger → Webhook. Set HTTP Method to
POST. Copy the Production URL; the Test URL only works while “Listen for Test Event” is active. - Options → Raw Body: on, so the Code node can verify the signature against the exact bytes. Respond decides what Atlas Labs sees. At “Immediately” n8n answers
200“Workflow got started” before any other node runs, well inside the5-second window, but a failure later in the workflow is then invisible to Atlas Labs. The other Respond modes (“When Last Node Finishes”, or a Respond to Webhook node) answer after the run, which must then finish inside those5seconds; what status a failed run returns in those modes is not something we have confirmed, so test it once with the ping and readGET /webhooks/{id}/deliveries. - Subscribe the Production URL with either key. On n8n Cloud the host is
*.app.n8n.cloud, which Atlas Labs recognises. Self-hosted: the URL must be publichttps://on port443, sohttp://localhost:5678/webhook/…is refused withport_not_allowed; put n8n behind a tunnel or reverse proxy and check n8n'sWEBHOOK_URLsetting, which decides the public address its Production URL shows.- Method
POST- URL
https://automationatlas.io/labs/api/v1/webhooks- Headers
Authorization: Bearer lab_off_YOUR_KEYContent-Type: application/json- Body
{"url": "https://<your-n8n-host>/webhook/atlas-labs-sales-order-accepted", "events": ["office.sales_order.accepted"]}
- Publish (activate) the workflow so the Production URL is registered, then send the test ping (
POST /webhooks/{id}/test). The subscription turnsactiveonce n8n answers2xx. - Verify and parse in a Code node. In a Code node placed directly after the Webhook node, headers arrive in
$json.headers; with Raw Body on, the unparsed string arrives under the body (community reports vary by version, so check once in your n8n). Paste theverifyfunction below without its twoimportlines and theexportkeyword, replaceprocess.env.ATLAS_WEBHOOK_SECRETwith the secret from a credential or variable you keep in n8n rather than awhsec_value pasted into the code, and throw when it returnsfalse; hownode:cryptois loaded inside an n8n Code node, and whether your instance allows it, is not something we have confirmed (n8n also has a Crypto node). Then parse the string so later nodes have fields:const event = JSON.parse(raw)and returnid,typeanddata.sales_order_id. With Respond at “Immediately” the throw only stops the execution; Atlas Labs already received200and will not retry. Whether the throw reaches Atlas Labs as a non-2xxunder the other Respond modes, and so triggers the retries after10 s,60 sand300 s, is not something we have confirmed.
snippets/07-verify-webhook.mjs excerpt: the header comment and the verify function, as in the tested file; the server part is cut where // … stands verified against the repo's own signer // Receive and verify an Atlas Labs webhook (Node 20+, no dependencies).
// Standard Webhooks: headers webhook-id, webhook-timestamp, webhook-signature.
// signature = "v1," + base64(HMAC-SHA256(key, `${id}.${timestamp}.${rawBody}`)), where key is
// the base64-decoded part of your whsec_ secret. During the 24 h after rotate-secret the header
// holds two space-separated values ("v1,<new> v1,<old>"); accept either.
import { createHmac, timingSafeEqual } from "node:crypto";
import { createServer } from "node:http";
const SECRET = process.env.ATLAS_WEBHOOK_SECRET; // whsec_...
const TOLERANCE_S = 300;
export function verify(secret, headers, rawBody, nowS = Math.floor(Date.now() / 1000)) {
const id = headers["webhook-id"];
const timestamp = headers["webhook-timestamp"];
const header = headers["webhook-signature"];
if (!id || !timestamp || !header) return false;
if (Math.abs(nowS - Number(timestamp)) > TOLERANCE_S) return false; // stale or replayed
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const expected = createHmac("sha256", key).update(`${id}.${timestamp}.${rawBody}`, "utf8").digest();
return header.split(" ").some((part) => {
const [version, value] = part.split(",");
if (version !== "v1" || !value) return false;
const given = Buffer.from(value, "base64");
return given.length === expected.length && timingSafeEqual(given, expected);
});
}
// … How do I call the Atlas Labs API from n8n?
- Add node → HTTP Request. Set Method and URL, for example
GET https://automationatlas.io/labs/api/v1/office/sales-orders/<id>to read the accepted order'squantityanddue_sim_day. - Authentication → Generic Credential Type → Bearer Auth. Paste only the key; n8n sends
Authorization: Bearer …. Or choose Header Auth with NameAuthorizationand ValueBearer lab_off_YOUR_KEY. Create two credentials, “Atlas Labs Head Office” and “Atlas Labs Factory”, and reuse them. - For a write, Send Body → JSON and Send Headers →
Idempotency-Keymapped to the Code node'sidoutput, or to the Webhook node's header by node name,$('Webhook').item.json.headers['webhook-id'](check the node name in your workflow); both are the event id. In this node$jsonis the previous node's output, the GET-order response, so$json.headers["webhook-id"]would be empty here. Set the key in a node before the request rather than with an expression that changes on each try, so retries reuse it.- Method
POST- URL
https://automationatlas.io/labs/api/v1/factory/work-orders- Authentication
- Bearer Auth credential holding
lab_fac_YOUR_KEY - Send Headers
Idempotency-Key= the Code node'sid(the event id)- Send Body (JSON)
{"sales_order_id": "<sales_order_id from the Code node>", "quantity": <from the order>, "due_date": <due_sim_day from the order>}
Preset matters. Pick a preset with a manual bridge (Standard or Full) or this write returns
409 already_bridged, because Starter bridges automatically. - Shortcut: Parameters → Import cURL. Paste a curl command from the REST page; it overwrites the node's configuration. Values import as strings, so switch the body from “Using Fields Below” to “Using JSON” to keep numbers as numbers.
- Treat
409 already_bridgedas success in an IF node (201or409witherror.code=already_bridged); anything else should fail the execution.
Idempotency and retries
- Money and stock routes are idempotent through
Idempotency-Key, so Retry On Fail is safe when the same key is sent on each try. - The node's Wait Between Tries is short, so for
429 rate_limitedread theRetry-Afterheader and use a Wait node, or space calls with HTTP Request Batching (Items per Batch, Batch Interval). The numeric limits of Max Tries and Wait Between Tries are not stated on the node's docs page. - A reported n8n bug (
1.35.0) skips retries when On Error is set to a Continue option; keep On Error at its default while retrying.
Common errors
| You see | Why, and the fix |
|---|---|
422 webhook_url_rejected, port_not_allowed | The URL has a port other than 443 (a local n8n on 5678). Expose it over public HTTPS first. |
422 webhook_url_rejected, hostname_not_public | localhost, a LAN name or a private IP. Use a tunnel or your n8n Cloud URL. |
403 wrong_system | The wrong credential on the node. Head Office routes want the lab_off_ credential, Factory routes the lab_fac_ one. |
401 unauthorized | A Header Auth credential whose Value lacks the word Bearer, or a key that is unknown, revoked or deleted with its sandbox. An expired sandbox answers writes with 423 sandbox_expired instead. |
422 idempotency_key_reused | The same key reached a different request. Map a stable value such as the event id. |
| Webhook never fires | The workflow is not active, or you subscribed the Test URL. Activate it and subscribe the Production URL. |
Gotchas
- No OpenAPI import. The HTTP Request node offers Import cURL only; community requests for OpenAPI import are open. Earlier Atlas Labs docs said otherwise; this page is the correction.
- Local n8n is refused. Atlas Labs delivers only to public HTTPS on port
443, never follows redirects, and reads at most1 KBof your response. - Raw body, no fields. Whether Raw Body exposes the exact bytes in your version is reported inconsistently in the n8n community; verify once with the test ping before relying on it. While it is on,
idanddata.sales_order_idare not mappable until the Code node parses the string; map them from that node's output. - Test vs Production URL. They differ; subscribe only the Production URL.
- Payloads are small. Atlas Labs webhook bodies are at most
4,096bytes, far under n8n's default limit.
Download the workflow
POST /factory/work-orders) → IF (201 or 409 already_bridged). Reads ATLAS_LABS_* variables you set in n8n. Needs a manual-bridge preset (Standard or Full). What the template needs. It reads its settings from $env.ATLAS_LABS_BASE_URL, $env.ATLAS_LABS_OFFICE_KEY, $env.ATLAS_LABS_FACTORY_KEY and $env.ATLAS_LABS_WEBHOOK_SECRET, and calls require('crypto') in a Code node, so it is written for a self-hosted n8n. From the second major version of n8n, N8N_BLOCK_ENV_ACCESS_IN_NODE defaults to true (its breaking-changes page says so), so set it to false for $env to work in the Code node; that page also advises credentials rather than environment variables for sensitive data. Importing built-in modules is off unless NODE_FUNCTION_ALLOW_BUILTIN is set, so set it to crypto. Code nodes run on task runners by default in that version; whether the main-process variable is enough with the default internal runner is not something we have confirmed, and with an external runner the setting belongs in the runner's own environment. n8n's Code node page says Cloud includes crypto, but whether you can set environment variables on Cloud is not something we have confirmed, so on Cloud you would replace the $env lookups with values you type in yourself. Leave the Webhook node's Raw Body option off: unlike the hand-built recipe above, the template verifies the signature against the parsed body re-serialised with JSON.stringify. Atlas Labs builds each webhook body with JSON.stringify, so it is compact JSON with no extra whitespace and re-serialising reproduces the same bytes. That n8n then delivers the parsed JSON at $json.body is not something we have confirmed against n8n's own documentation.
Questions people ask
- Why does my local n8n webhook get
port_not_allowedorhostname_not_public? - Atlas Labs only delivers to public HTTPS on port
443. Use n8n Cloud, or put self-hosted n8n behind a tunnel or reverse proxy with a public hostname. - How do I import the workflow?
- Download the JSON above and import it from the n8n editor, then open each HTTP Request node and attach your credentials. The file was checked for n8n's export shape but has not yet been imported into a live n8n; tell us if a node needs a nudge.
- Can n8n import the Atlas Labs OpenAPI file?
- No. The HTTP Request node only imports a curl command. Paste one from the REST page.
- Which credential type should I use?
- Bearer Auth, with the key alone. Header Auth works too if you type
Bearerbefore the key yourself. - Does the Webhook node verify Standard Webhooks signatures?
- No. Its own Authentication options (Basic, Header, JWT) are for callers who can send credentials, which Atlas Labs does not. Verify in a Code node as shown.
- Why didn't Atlas Labs retry when my Code node threw?
- Because Respond was “Immediately”, so n8n had already answered
200. The other Respond modes (“When Last Node Finishes”, or a Respond to Webhook node) answer after the run; we have not confirmed what status a failed run returns in them, so test once with the ping and checkGET /webhooks/{id}/deliveries. Either way the run must finish within5seconds.
Sources (checked 15 Oct 2026): docs.n8n.io, HTTP Request node, HTTP Request credentials, Webhook node, node settings (Retry On Fail), HTTP Request common issues, rate limits. Atlas Labs facts from the API source, observed live.