Package Information
Available Nodes
Documentation generatingβ¦
Documentation
Pilot Status - n8n Integration
This is the official n8n node for Pilot Status β a WhatsApp transactional, marketing, and bulk messaging platform.
With this integration, you can seamlessly connect your n8n workflows to Pilot Status to send messages, connect WhatsApp numbers, and react to incoming messages or status changes in real time.
Not every endpoint is a node operation, and that is deliberate. In an n8n node the cost is not the HTTP call β it is the UI: every field needs a control written and positioned by hand, and every optional one needs a hidden twin to undo n8n's empty-value behaviour. So this node covers the operations a workflow author actually reaches for. For anything else in the public API β templates CRUD, group writes, Chatwoot, webhooks CRUD, billing β use n8n's built-in HTTP Request node with the same Pilot Status API credential: it is header auth, so it works out of the box. That is the supported escape hatch, not a workaround.
π¦ Installation
To install this node in your n8n instance, follow these steps:
For n8n UI (Community Nodes)
- Go to Settings > Community Nodes.
- Click on Install.
- Enter
n8n-nodes-pilot-status. - Click Install.
For Docker / Self-hosted
If you are hosting n8n yourself, you can install it via npm:
npm install n8n-nodes-pilot-status
Make sure to restart your n8n instance after installation.
π Authentication
To use the Pilot Status node, you need an API Key.
- Go to your Pilot Status Dashboard.
- Create a new API Key.
- In n8n, add a new Pilot Status API credential and paste your API Key.
The credential has two optional fields:
- Base URL β API root without
/v1. Leave it alone unless you are pointing the workflow at a staging environment. It is used by the node, by the credential test and by the dropdowns (templates, groups, newsletters), which used to always hit production. - WhatsApp Number ID β sent as
x-whatsapp-number-idon every request. Fill it in when your key is tenant-scoped and the operation is per-number; a number-scoped key already carries its number, and the header is ignored.
Note: TEST keys will only send messages to the WhatsApp number registered in your Pilot Status Profile.
β‘ Triggers (Webhooks)
The Pilot Status Trigger node allows your workflows to react to real-time events.
Registration: Automatic (recommended). Select the event, pick your Pilot Status API credential, and set Registration to Automatic. Activating the workflow creates the webhook subscribed to that event only β so unwanted events are never delivered β and deactivating it deletes the subscription.
Registration: Manual (default, unchanged). Nothing is created for you:
- Add the Pilot Status Trigger node to your workflow.
- Select the event you want to listen to (or select "All Events").
- Copy the Webhook URL provided by the node.
- Go to the Webhooks section in your Pilot Status Dashboard and paste the URL.
In this mode the event filter runs after delivery: the payload has already reached n8n, and non-matching events are answered 200 Ignored event.
β οΈ Verify the signature
Every delivery from a webhook that has a secret carries x-pilot-status-signature: HMAC-SHA256 hex over the raw body. Paste that secret into the trigger's Signing Secret field and any delivery that does not match is rejected with 401. Leave it empty and anyone who learns your n8n webhook URL can fire this workflow with a hand-written payload.
The API never returns the secret β not even in the 201 of the create call β so it cannot be filled in for you, including in Automatic registration. Copy it from the dashboard when the webhook is created; it is shown once.
Available Events:
- Message Sent (
message.sent): Triggered when the message is sent to the provider. - Message Delivered (
message.delivered): Triggered when the message is successfully delivered to the device. - Message Read (
message.read): Triggered when the user reads the message. - Message Failed (
message.failed): Triggered if the message fails to send (includes error details). - Message Reply (
message.reply): Triggered when a user replies specifically to an automated message. - Message Received (
message.received): Triggered when a general inbound message is received on your connected WhatsApp number. - Group Message (
message.group): Triggered when an inbound group message is received (group subject is included when available). - Newsletter Message (
message.newsletter): Triggered when an inbound WhatsApp channel / newsletter message is received. - Number Created / Connected / Disconnected / Removed / Restored (
number.created,number.connected,number.disconnected,number.removed,number.restored): Lifecycle of a WhatsApp number in your account.number.restoredmeans a deleted number was brought back within its 30-day window β it returns disconnected and must be reconnected. - Call Ringing / Connected / Ended / Missed / Permission Updated (
call.ringing,call.connected,call.ended,call.missed,call.permission_updated): WhatsApp Business Calling events (call.*) β Meta Cloud API and web (Pilot Status) numbers (call.permission_updatedis Meta-only). Payloads are flat (fields next toevent, nodatawrapper):{ event, callId, externalCallId?, direction, status, from, to, timestamp, duration? };duration(seconds) appears oncall.endedonly when the call was answered.externalCallIdis the provider call id (Metawacid...or Evolution GO CallID). - Flow Response Received (
flow.response_received): Triggered when someone SUBMITS a WhatsApp Flow form β the answers arrive here. META numbers only. Wrapped indata:{ event, data: { event, flowId, metaFlowId, flowToken, messageId, whatsappNumberId, from, receivedAt, response, responseRaw, flowMedia, submitted } }. Four things to know. The number field iswhatsappNumberId, notnumberIdβ same value, different name on the wire.responseandresponseRaware mutually exclusive:responseholds what your own Flow endpoint returned, already parsed, andresponseRawisnull; if the parse failed it is the other way round, and the raw string is the only copy of the answer there is. On a Flow with adata_exchangeendpointresponseis EMPTY even on a number that stores everything β Meta's completion payload carries only theflow_tokenβ and what the person typed is insubmitted: one entry perdata_exchangescreen,{ screen, at, data, omitted }, in order, whereomitted(DATA_TOO_LARGE,LIMIT_REACHEDorUNREADABLE) says why adataisnull.flowMediaholds the files attached inside the form:{ field, fileName, mimeType, status, url }. Both arenulland never[]when empty, so a node testing for an empty array never fires;GET /v1/flows/{id}/responsespublishes the same two lists as arrays, undermediaandsubmitted. And picking this event for a non-Meta number fails SILENTLY: the API filters a webhook's events down to what the number's provider can emit, so the subscription is created with an empty event list, returns201, and never fires. - Lead Received (
lead.received): A lead from a Meta Lead Ads Instant Form (Facebook / Instagram), delivered on the WhatsApp number whose Page is linked in the dashboard β any provider, not only Meta numbers. Wrapped indata:{ event, data: { leadId, leadgenId, whatsappNumberId, pageId, pageName, formId, formName, adId, adsetId, campaignId, adName, adsetName, campaignName, platform, isOrganic, createdAt, fullName, email, phone, fields, consentGiven, conversationId } }. The number field iswhatsappNumberId, notnumberId.leadIdis the Pilot Status id (the one Lead β Get takes);leadgenIdis Meta's, unique per lead β use it to deduplicate.createdAtis when the person submitted the form.fieldsholds every answer as{ key, label?, values }, andvaluesis always a list, even for a single answer.labelis absent when it could not be resolved; on Lead β Get / Get Many it is always present, falling back to the key.consentGivenistrue/falseonly when the form has a consent checkbox configured;nullmeans unknown, never a refusal.
Webhook payload notes:
- Customer webhook payloads do not include:
projectSlug,lastMessageId. OptionalcorrelationId(same as HTTP 202 when present) may appear on outbound status events and onmessage.reply/message.receivedwhen correlated to a prior send. message.replyalways includesmessageRepliedId: Pilot Status internal id of the original outbound message when correlated, ornullif none was found β usequotedMessageIdwithmessage.sentβsmessageIdwhen it isnull.message.receivedincludesfromMe(boolean) andfromName(string, optional).- For outbound status events (
message.sent,message.delivered,message.read,message.failed):messageIdis the WhatsApp provider id (may benull) andinternalMessageIdis the Pilot Status message id. - Events newer than this node version never break a workflow. The trigger does not validate the body against a list of known events: with All Events selected, an event added to the platform after your node version still fires the workflow with the body untouched (branch on
eventand ignore what you do not handle); with a specific event selected, anything else is answered200 Ignored event.
π Actions (Operations)
The Pilot Status node provides several resources and operations to manage your account and send messages.
1. Message
- Send: Send a template message (OTP, Utility, or Marketing). Choose Phone Number or WhatsApp Group; for groups use
groupId(JID ending in@g.us) instead ofdestinationNumber. Optional dropdown loads groups fromGET /v1/groupswhen the WhatsApp session is connected. Variables can be provided via JSON or the UI collection (including placeholders used in button labels). - Get Status: Retrieve the current status of a specific message using either its Pilot Status message id or WhatsApp provider id.
- Get History: Read the conversation history of the number, newest first β both directions, every provider, including messages imported from the device on connect. Filters by Start Date / End Date over the provider timestamp (when the message happened, not when it was ingested). This is how you fetch old messages: the webhook does not replay history by default. Requires a number-scoped API key.
- Get Many: The message log β sent and received, with delivery status, newest first. Different from Get History: this is the operational trail (
status,errorCode,origin, whether the 24h window was open at send), not the chat thread. Filters: Direction, Status (comma-separated), Page, Page Size. For date/search/template filters use the HTTP Request node. - Get Unread: Unread inbound messages. β οΈ
totalis the real unread count from the conversation counters β not the length of the returned list, which carries only the latest unread per conversation. - Mark as Read: Blue ticks for a contact's last inbound message and every earlier one, without sending anything. Best-effort: a
200withacked: falsemeans there was nothing to acknowledge, or the number isRELAY_ONLY. - Send Typing Indicator: Show "typing"/"recording" to a contact. It clears when a message is sent or after a provider timeout (~25s on Meta). On META numbers
recordingis shown astypingandpausedis a no-op β readeffectiveStatein the response for what actually happened. - Cancel: Cancel scheduled (not-yet-sent) messages by id.
2. Number
β οΈ Delete changed meaning. It used to destroy the number and free the phone for immediate reconnection. It is now a 30-day soft delete: the device is logged out, the row survives, and the response carries
restorableUntil. Use Restore to undo it (you must reconnect afterwards) or Purge for the old, irreversible behaviour. Deleting still does not reduce paid capacity β the freed slot stays available to reconnect another number this cycle.
- Create: Provision a new WhatsApp number instance. It returns a base64 QR Code and optional
pairingCode(letter code) for WhatsApp βlink a deviceβ. By default, the number is linked to the API Key used in the request. - Connect (QR + pairing code): Regenerate QR and pairing code for an existing instance (only when not
OPEN). - Get Status: Check the connection status of a WhatsApp instance (e.g.,
CONNECTING,OPEN). - Get: Full detail of one number, including the
settingsblock and β for official Meta numbers β the nestedmetablock. - Delete: Soft-delete (30 days, reversible β see the warning above).
- Restore: Undo a soft-delete inside the window. Gives back the row, not the connection: run Connect afterwards (
requiresReconnect: true). - Purge: Irreversible hard delete. Cascades chat, messages, templates and keys, and tears the number down at Meta.
- Logout: Unpair the device without deleting anything β id, keys, webhooks and history are kept.
- Check on WhatsApp: Whether a phone exists on WhatsApp. Non-official (Evolution) numbers only β a Meta number answers
400 NOT_SUPPORTED_FOR_META. For Brazilian mobiles the 9th-digit variant is probed too, and the returnednumberis the registered form to send to, which may differ from what you passed. - Update Settings: Per-number settings. This is the only settings endpoint: there is no
POST /numbers/{id}/settings, and Evolution'ssyncFullHistorydoes not exist here. Partial β only the fields you add to the collection change.- History Import Enabled / Webhook Historical Messages apply to every provider. The second is off by default: each reconnect replays up to 30 days of conversation, and your integration cannot tell it apart from new traffic (on the FREE plan it also consumes inbound quota). To read history, use Message β Get History.
- Always Online, Reject Call, Reject Call Message, Read Messages, Ignore Groups, Ignore Status are the Evolution GO advanced settings, in the GO dialect (
ignoreGroups, not the v2groupsIgnore). They are tri-state: Inherit From Provider leaves the field untouched, Reset to Provider Default clears the stored value. On a Meta number they are stored but never applied.
3. API Key
- Regenerate: Regenerate the default API Key of a single WhatsApp number (tenant-scoped), identified by its
whatsappNumberId. The previous key for that number is rotated and the new key is returned. - Get Many: List API Keys. With a tenant key, this reveals every number's real API key.
4. Analytics
- Get Dashboard: Retrieve your messaging statistics (Total Sent, Total Failed, Failure Rate, and Daily charts) based on your timezone.
6. Call (WhatsApp Business Calling β Meta Cloud API + web/Pilot Status numbers)
- Initiate: Start a business-initiated voice call (BIC) β provide the destination phone. On Meta numbers also provide the SDP offer (RFC 8866) from your WebRTC client and a prior user call permission; BIC minutes are billed by Meta directly on your WABA (per minute, 6-second pulses, only when answered). On web (Pilot Status) numbers leave the SDP empty β media is handled server-side, no permission step and no Meta billing. Pilot Status does not charge for calls.
- Accept: Answer an inbound (user-initiated) call, after the
call.ringingwebhook. On Meta numbers pass the business-side SDP answer, built from thesdpOfferthat Get returns with Include SDP; on web numbers leave it empty. - Reject: Decline an incoming call.
- Get: One call. Turn on Include SDP to also get
sdpOffer/sdpAnswerβ alwaysnullon web numbers. - Get Many: The number's calls, newest first (
Limit,Beforecursor). - Terminate: Hang up an active call by its Pilot Status id (
call_...) or provider call id (Meta wacid / Evolution GO CallID). - Play (web numbers only): Play an audio file into an active call by public URL (
.mp3/.wav/.opus). Queued and played on connect when the call is not active yet. Meta numbers return400 FEATURE_NOT_SUPPORTED. - Realtime Session (web numbers only): Open a short-lived full-duplex realtime media session β returns
{ wsUrl, token, expiresInSeconds }. Connect a WebSocket towsUrland exchange raw binary PCM16 LE audio frames (plain WebSocket transport, NOT WebRTC; single-use token, ~2 minutes).talksends and receives audio;listenonly receives. - Get Settings / Update Settings (Meta only): Read the number's
callingsettings object or enable/disable calling. - Get Permission / Request Permission (Meta only): Check whether the business may call a user, and send the interactive call-permission request (inside the 24h service window). The user's reply arrives as the
call.permission_updatedwebhook.
5. Service Window
- Get: Whether the Meta 24h customer-service window is open for a contact. β οΈ Branch on
windowType, not onopen: on a META numberwindowTypeisMETA_24Handopenis a real constraint (outside the window only an approved template goes through); on a web/unofficial numberwindowTypeisNONEandopenis alwaystrue, because the Meta rule does not exist there.
8. Lead (Meta Lead Ads β Facebook / Instagram Instant Forms)
Read-only, and requires a number-scoped API key: a lead belongs to the number whose Page is linked in the dashboard.
- Get Many:
GET /v1/leadsβ the number's leads, newest first by the time the person submitted the form. Filters: Form ID, Ad ID, Page ID (exact match), Start Date / End Date (Meta'screated_time, not when Pilot Status received the lead), Page, Page Size (max 100). Answers{ leads, total, page, pageSize, totalPages, notice? }. - Get:
GET /v1/leads/{id}β one lead by its Pilot Statusid(=data.leadIdon thelead.receivedwebhook, not Meta'sleadgenId). Answers{ lead, notice? }; a lead of another number is404 LEAD_NOT_FOUND.
Each lead: { id, leadgenId, whatsappNumberId, pageId, pageName, formId, formName, adId, adName, adsetName, campaignName, platform, isOrganic, createdAt, fullName, email, phone, fields, consentGiven, conversationId, autoSendStatus, autoSendReason, source, redacted }, with fields as { key, label, values }. β οΈ consentGiven: null means unknown (no consent checkbox configured, or not answered) β never read it as a refusal. On a RELAY_ONLY number a lead keeps its ids, attribution and status but comes back with redacted: true and no name, e-mail, phone or answers, and the response carries notice: "PII_RELAY_ONLY".
7. Phone Line (identity verification + activation audio)
Requires a workspace (tenant-scoped) API key β a number-scoped key is refused. The verification operations need the OWNER role (phone_lines:purchase); the audio needs phone_lines:read. Errors come back as { error, code }.
- Get Verification: The workspace verification state β
status(NONE,PENDING_CONTACT,NEEDS_DOCUMENT,IN_REVIEW,APPROVED,REJECTED),canPurchase, andverification.contact.requiredChannels/confirmedChannels/pendingChannels(UPPERCASE there:EMAIL,WHATSAPP). Personal data comes back masked. - Start Verification:
POST /v1/phone-lines/verificationwith Document Type (CPF/CNPJ), Document Number, Contact Email, Contact WhatsApp (with country + area code) and the document (PDF/JPG/PNG/WEBP, up to 10 MB). Codes go out to the required channels on their own.- Idempotency Key (required) is sent as the
Idempotency-Keyheader. β οΈ Reuse the same key when retrying the same submission β the API then answers with the verification it already created (200,Idempotent-Replayed: true) instead of starting another. Map it from your own data (e.g. an id of the input item); a key that changes on every run makes retries unsafe. Same key + different body β409 IDEMPOTENCY_KEY_REUSED; while the first request is still running β409 IDEMPOTENCY_KEY_IN_USE. - While
PENDING_CONTACT, a new start replaces the current one (the old codes stop working). Limit: 10 submissions per hour per workspace (429 RATE_LIMITED).
- Idempotency Key (required) is sent as the
- Send Contact Code: Resend a code on a channel (
whatsapporemail). Codes expire in 10 minutes; 60 s cooldown per channel (429 CONTACT_CODE_COOLDOWN). - Confirm Contact: Confirm a channel with the received Code. The last required channel may move the verification to
APPROVEDorIN_REVIEW. 5 wrong attempts lock the code (429 CONTACT_CODE_TOO_MANY_ATTEMPTS). - Submit Document: Send a new document when
document.statusisUNREADABLE/UNCLEARor the status isNEEDS_DOCUMENT. Same file rules and hourly limit as the start; no idempotency key. - Download Activation Audio: The recording of an activation call (
activationIdfrom thephone_line.audio_received/phone_line.code_receivedwebhook), returned as n8n binary data in the field set by Put Output File in Field (defaultdata), with the MIME type fromContent-Typeand the file name fromContent-Disposition. The JSON part carries only{ activationId, contentType, contentLength, fileName }. β The recording speaks the activation code β treat the file as a credential (do not post it to chats or logs). An unknown id, a line from another workspace, a missing recording or a returned/canceled line all answer404 ACTIVATION_NOT_FOUND.
Document input β two options (Document Source):
- Binary Field (default): the name of a binary property of the input item (e.g. the output of Read/Write Files from Disk, HTTP Request or a form upload). It is sent as
data:<mimeType>;base64,<data>. - Data URI: a ready
data:<mime>;base64,...string.
Phone-line webhooks (
phone_line.*) live in a separate registry with their ownplwh_...secret, and the Pilot Status Trigger does not register them: its Automatic mode creates subscriptions in the numbers webhook registry, which never deliversphone_line.*. To receive them, create the phone-line webhook in the dashboard and point it at a regular n8n Webhook node (verifyx-pilot-status-signature, HMAC-SHA256 of the raw body).
π‘ Use Case Examples
Here are some ideas of what you can build with this integration:
- Abandoned Cart Recovery: Trigger a workflow from your e-commerce platform (like Shopify or WooCommerce) to send a Pilot Status message with a discount code 1 hour after cart abandonment.
- Customer Support Routing: Use the Trigger node to listen for
Message Received. If the customer asks for help, create a ticket in Jira or Zendesk automatically. - SaaS Onboarding: When a new client signs up on your app, automatically generate an API Key and provision a WhatsApp Number for them using n8n.
- Appointment Reminders: Connect Google Calendar to n8n and use the
deliverAtfield in the Send Message operation to schedule a reminder 24 hours before the event.
π Documentation & Support
- API Documentation: https://pilotstatus.com.br/docs
- Official Website: https://pilotstatus.com.br
If you have any questions or need help, feel free to reach out to our support via the dashboard!