Where reading stops and doing begins
Part 5 left you with flows that ask Home Assistant questions — what state is this entity in, has it changed. Every node in that part reads. This part introduces the node that writes: the Action node, in the pinned build this guide follows (HA WebSocket integration 0.80.3, node version 7), and the four different ways a flow can be woken up before it ever reaches one. Node behavior here can change between builds of the HA integration, so treat every specific field name and default as tied to that pinned version, not as a permanent promise. Because a mistake in this part is not a wrong number on a Debug panel — it is a light switching on, a notification going out, a helper changing state in a house nobody asked to change — every example below stays disabled and every target stays an obvious placeholder. This is the anatomy lesson, not the live wire.
Everything before this part could only look. This part can touch.
The Events: state and Current State nodes from Part 5 primarily read data. An Action can control devices, change helpers, send notifications, or call other Home Assistant functionality — and a single wrong target, an allowed payload override, or mistaking the reconnect queue for a reliable work queue can turn one test into several real operations.
Every Action call answers exactly three questions, and the three answers belong in three different fields. Action says which Home Assistant operation to run. Target says which entity, device, area, floor, or label it reaches. Data carries whatever arguments that specific action needs. Node-RED will not stop you from putting an entity ID in the data field or message text in the target field — it will just send something that does not do what you meant.
It is ordering at a restaurant. The action is what you want done — "bring the check." The target is which table gets it. The data is anything extra you tack on — "no ice, and a receipt." Say "bring the check" to the wrong table and the food still comes; it just comes to the wrong people. Nothing about the sentence being grammatically correct saves you from that.
In the source material's own worked example, the Action node ships disabled — "d": true in the flow JSON — with the Server, action, and entity all set to obvious placeholders. That is not a rough draft waiting to be finished. That is the state it is supposed to stay in until a security review, the target owner's approval, and a recovery plan all exist. This part explains the contract well enough that you could pass that review. It does not hand you anything ready to Deploy.
PLACEHOLDER_HA_ACTION, PLACEHOLDER_ENTITY_ID, and the like — because that is what the underlying source material itself uses, and because a placeholder that obvious cannot be mistaken for something you can paste into a live automation. If you build a real Action afterward, every placeholder gets replaced only after the review this part describes, not before.One label in the palette, a different name in the file
In the pinned build, the palette shows Action and the node type key is Action — but the type recorded in exported flow JSON is still api-call-service. "Call Service" in older flows and documentation is the same node under its legacy name. Do not revert the current terminology, and do not hand-edit the JSON type to action; the persisted type is the correct one, not a bug to fix.
After it parses the action string, the node splits it at the first period into a domain and a service — light.turn_on becomes domain light, service turn_on. If either half is missing, the action format is invalid. An action taken from configuration is rendered with Mustache first and then lowercased. The v7 migration keeps its older compatibility fields, but a new design should use the single action field rather than separate domain and service fields supplied by a message.
The target combines the five configured selectors with an allowed msg.payload.target; data is merged according to the JSON-or-JSONata, context, and payload rules covered in the next section. Once everything is merged, the node calls the Home Assistant WebSocket call_service operation.
flowchart LR A["action: domain.service"] --> M["Merged into one call"] T["target: entity / device /
area / floor / label"] --> M D["data: arguments for
that specific action"] --> M M --> C["HA WebSocket call_service"]
| Layer | Persisted field | Responsibility | Safe default |
|---|---|---|---|
| Operation type | action | An HA action in domain.service form | PLACEHOLDER_HA_ACTION; reject input overrides |
| Target | entityId, deviceId, areaId, floorId, labelId | Select the scope of the call | One minimal placeholder; no broad default scopes |
| Arguments | data / dataType | Action-specific arguments | Fixed schema and types; reject extra fields |
| Dynamic merging | mergeContext, mustacheAltTags | Context, or a JSON template / JSONata | Leave blank unless needed; restrict keys and owners |
| Execution boundary | queue, blockInputOverrides | Disconnected behavior and message overrides | none, true |
| Output | outputProperties | Map sent data, results, or other values | Only fields you need — never secrets or full attributes |
The safe baseline in the review draft uses exactly that: v7, queue set to none, Block Input Overrides set to true, debug logging off, placeholders as the only targets, and the whole node disabled. Its data field is a static illustrative string. That does not mean the action actually accepts that argument — and importing the flow does not send it.
Block Input Overrides decides whether a message can rewrite the call
The node can read msg.payload.action, msg.payload.target, and msg.payload.data from an incoming message; older flows also recognize a payload domain and service pair. When Block Input Overrides is true, all three of those overrides are disabled: action comes from configuration, target comes from the five configured selectors, and payload data does not take part in data merging at all. Every Action should keep this setting on.
If it is false, a payload action can replace the configured action, a payload target is deep-merged with the configured target, and payload data wins the merge outright. Even if the node upstream only meant to change one text field, a faulty node — or a malicious one — could change the action and its target together. For anything arriving over HTTP, MQTT, a webhook, a Dashboard, Assist, or another flow, type checking alone is not enough. You need an action allowlist, a target allowlist, and a data schema, and you need to build a new, clean msg object from them rather than trust the one that arrived.
flowchart TD
A["A message reaches the Action node"] --> B{"Block Input Overrides"}
B -->|"true, the safe default"| C["action, target, and data all
come from configuration.
msg.payload plays no part"]
B -->|"false"| D["msg.payload.action can replace the
configured action. msg.payload.target
is merged in. msg.payload.data
wins the data merge"]
true branch is the one this guide asks you to keep; false is what changes the moment you turn it off.Data can be entered as JSON or JSONata. JSON mode runs a Mustache render before parsing, and the optional alternate-tags setting only changes the template delimiters, nothing else. JSONata runs through the Node-RED evaluator. Neither one is Home Assistant Jinja, and neither one is JavaScript — do not carry syntax across from one to the other. Never treat an untrusted string as a template, and never place a token, a header, or a credential inside data or context.
mergeContext names one flow or global context key. The node reads the global object first, overlays the flow object on top of it, and — only when input overrides are allowed — overlays payload data last; configured data sits at the bottom of that stack with the lowest precedence. Setting Block Input Overrides to true only removes the payload layer from this stack; a flow or global value can still override your configuration. Before you turn mergeContext on for anything, decide who is allowed to write that context key, define its shape and how long it is kept, and write down how it gets cleared.
Block Input Overrides is the difference between a fixed menu and a customer who can swap any ingredient. With it on, the kitchen cooks exactly what is printed on the card, no matter what the runner shouts through the hatch. With it off, the runner's shout wins — table number included, whether or not the runner heard the order correctly.
Only one of the five target types stays the size you picked
The Target selector supports five ID types, and each configuration field becomes an HA target key on the wire: floor_id, area_id, device_id, entity_id, and label_id. An empty configuration array leaves that key out of the call entirely. At runtime a single entry can render as a plain string while several entries stay a collection. Every one of these values can render environment variables or Mustache, so environment and context data belong inside the same review boundary as the target itself.
| Target type | Placeholder | Scope risk | Review question |
|---|---|---|---|
| entity | PLACEHOLDER_ENTITY_ID | Most precise, but renaming or swapping the ID breaks it | Is this a dedicated test entity, and does it permit this action? |
| device | PLACEHOLDER_DEVICE_ID | One device can hold several entities and capabilities | Is the action's actual scope on the device clear? |
| area | PLACEHOLDER_AREA_ID | Membership changes as entities and devices are reassigned | Could new members be swept in without review? |
| floor | PLACEHOLDER_FLOOR_ID | Broader than an area, and may span several spaces | Is a whole floor really necessary? Deny it by default. |
| label | PLACEHOLDER_LABEL_ID | Label membership can grow dynamically | Who can change the label, and is that change watched? |
Being selectable in the editor does not mean every action accepts every target type. The Target field is filtered or hidden by Home Assistant's own service metadata, and an unfamiliar action may not carry reliable metadata at all. Check the action's documentation and services metadata for your Home Assistant version, and put the entity ID exactly where that action's schema says it belongs — do not invent a field from a screenshot, and do not lean on a compatibility shortcut you have not confirmed.
entity_id in their data fields rather than in target. When the service metadata shows fields.entity_id, data does not already contain entity_id, and the merged target.entity_id is a single string, the node warns and quietly moves that value into data for you. This compatibility path never moves an array target, and it is documented as scheduled for removal in a future 1.0 release — do not build a design that depends on it, and fix any flow that currently relies on it before upgrading.Several target types can be selected at once, and combining them can widen the scope rather than narrow it — it is a union, not an intersection. A safe draft picks one, most precise target type at a time. Treat an area, a floor, or a label as a group whose membership can change, and recheck it before every call rather than trusting what it held last time you looked. Debug output, documentation, and issue reports should show placeholders only, never a real ID.
What happens to a call when Home Assistant is not there to take it
Queue settings only matter while Home Assistant is disconnected. none raises a connection error immediately. first keeps only the first message that arrives while the queue is empty. last replaces the retained message with whatever arrives next. all appends every message to an in-memory array. Once the connection is ready again, the node retrieves entries with pop(), so a queue of several messages is drained from the end of the array — do not assume first-in, first-out order. None of the four modes is durable, none has a hard limit this guide can verify, and none guarantees a message is delivered exactly once.
flowchart TD Q["A call is made while
Home Assistant is unreachable"] --> N["queue: none
raises a connection error immediately"] Q --> F["queue: first
keeps only the first message"] Q --> L["queue: last
keeps replacing it with the newest"] Q --> AL["queue: all
appends every message in memory"]
| Queue | While disconnected | Recommendation |
|---|---|---|
none | Raises an error immediately | Safe default; do not defer control |
first | Keeps the first message, discards the rest | Only if the first intent is still guaranteed timely at reconnect |
last | Keeps overwriting with the newest message | Needs a version or timestamp check before it is acted on |
all | Keeps every message, unbounded, in memory | Do not use for control Actions |
It is the difference between four receptionists. "None" hangs up on the caller and tells them to try again themselves. "First" takes exactly one message and refuses every other call until that one has been delivered. "Last" keeps tearing up the old note and writing down only the newest one. "All" pins every note to a spike in the order they land — and hands them back in whatever order they come off the spike, not the order they were written.
If your flow genuinely needs a reliable work queue, that calls for a dedicated design with persistence, ordering, deduplication, deadlines, cancellation, and a dead-letter path — the Action node's four queue options are not a substitute for any of that. After a reconnection, query Current State again rather than blindly replaying whatever is sitting in the queue.
Response support depends on the action, not on the node
Do not assume every action returns data, or that a result always lands in msg.payload. A destination property is written only when Output Properties explicitly maps a value to it.
response, the outgoing call_service request sets return_response to true; without response metadata it sets the flag to false. On older Home Assistant it does not set the value at all. This guide's own baseline still assumes Home Assistant 2024.3 or newer — this detail only explains the runtime boundary underneath that assumption.Once the call resolves, the node exposes response?.response as the Output Properties source named results, and it combines the domain, service, data, and target actually sent as the source named sentData.
| Output source | Contents | Recommended mapping |
|---|---|---|
sentData | Domain, service, data, and target actually sent | Never output the whole object; extract only a sanitized correlation field |
results | The response part of the HA call, if any | Write to msg.actionResult, then validate its shape |
msg | The input message, still available for mapping | Keep only necessary, allowlisted fields |
| No output properties set | The original message is sent on after success | Do not assume the unchanged payload is a response |
return_response is a WebSocket call flag the wrapper chooses from the action's own metadata; it is not a general-purpose field you put into data yourself. Verify actual behavior against the official documentation and the services metadata for whichever Home Assistant version you are running.
Common failures include being disconnected with queue set to none, an invalid action format, unparseable JSON, a schema-invalid input, a rejected HA call, or a connection failure mid-call — none of these produce a successful output mapping. Route them through Catch and watch Status, and end the error branch in a bounded Debug or alert rather than an automatic retry. Never change the target automatically and try again, and never write full data, responses, tokens, or real IDs to a log.
A retry policy has to account for whether the action is idempotent. A network timeout can happen after Home Assistant has already executed the action but before Node-RED receives the response, so an immediate retry can duplicate the side effect. A safe design needs a request or correlation ID, an action-specific way to verify the outcome, a retry limit, backoff, and a manual recovery path — not just a check for whether any output arrived.
The word "trigger" hides four genuinely different sources
Judged by node names alone, it is easy to assume Events: all, Device, and Time all just "send a message when something happens." In practice, Events: all subscribes to Home Assistant events or to the WebSocket client's own lifecycle events; Device can run in Trigger or Action mode; Time schedules messages from the state or an attribute of one specified entity; and Calendar polls a calendar and schedules timers from it. Picking the wrong entry point can flood a flow with events, repeat an operation after reconnection, or quietly turn a read-only flow into one that touches devices.
| Model | Observed source | Suitable use | Primary boundary |
|---|---|---|---|
| Event bus | An HA event type and its event data | Discrete events that are not one entity's state | A blank event type can receive a very high volume; event data may carry identifying information |
| Entity state | The old and new state of state_changed | Doors, windows, lights, sensor readings, and similar transitions | Attribute updates fire too; unknown and unavailable need separate handling |
| Device automation | A trigger or action the device's own integration supplies | Button gestures or capabilities that integration explicitly exposes | Capabilities depend on the integration and device — not every device offers the same options |
| Time scheduling | A time value in an entity, a calendar item, or a Node-RED timer | Producing a message at a future time | Time zones, DST, restarts, expired times, and data updates can all change the result |
flowchart TD S["Something happens"] --> E["Event bus:
a Home Assistant event type"] S --> ST["Entity state:
old_state / new_state of state_changed"] S --> DV["Device automation:
a trigger the integration defines"] S --> TM["Time scheduling:
an entity's time, a calendar item,
or a Node-RED timer"]
An event name is not an entity ID. An event type selects event-bus messages, an entity ID identifies a state, a device ID identifies an entry in the device registry, and tag IDs and webhook IDs live in their own separate namespaces again. Do not interchange identifiers because the strings happen to look similar, and do not treat a display name in the UI as a stable ID.
Receiving and sending carry different risk. Events: all, Tag, and Zone are input nodes. Fire Event sends an event to Home Assistant. Device in Action mode sends a device action. Sentence in response mode — and a trigger-mode Sentence configured with a fixed response or a timeout fallback — answers the voice assistant. Time entity in set mode writes an entity value. In any test flow, every sending or responding configuration should stay disabled with nothing wired into it.
Events: all — only with an explicit event type
Events: all accepts one Event Type, or, left blank, receives every event on the bus. Both the editor and the official documentation warn explicitly that a blank value can overwhelm the WebSocket message queue. Event data has to be a JSON object, and at runtime it is matched as a subset of the actual event content — useful for narrowing a known event further, but not a substitute for validating the input downstream.
The special event type home_assistant_client carries client lifecycle events — a data-loading or connection phase, not a business condition being met. Do not build a gate that waits for a connected event.
The store's lights turning back on does not mean the shelves are stocked. "Running" and "ready" are the lights coming on. They do not tell you whether the sensor reading you cared about this morning is still what it was — that is a separate trip to go check, not something you get to assume from the lights alone.
states_loaded, services_loaded, running, and ready. The unwired connected handler is not part of this node's real output contract. "Output only after Home Assistant is running" suppresses ordinary events until then, but client events are emitted on a separate path, so a reconnection branch needs its own handling regardless.sequenceDiagram participant HA as Home Assistant participant N as Node-RED (Events: all) participant F as Your flow HA->>N: states_loaded HA->>N: services_loaded HA->>N: running HA->>N: ready N->>F: flow health signal only F->>HA: Current State query, made separately HA->>F: the actual value, checked fresh
running or ready events as a health signal, query the entities you need with Current State, check their timestamps and a deduplication key, and stop if the state is missing. Lifecycle events may update your flow's own health status only — they must never directly fire a catch-up Action.Sentence — triggers and responses travel in opposite directions
Sentence supports trigger and response modes, and both require the separately installed hass-node-red custom integration on the Home Assistant side (this guide makes no assumption about which version of that integration you run). In trigger mode, Home Assistant sends the sentence, the match result, a device ID, and a response ID; the output message carries an internal _sentence_response_id. Response mode reads that ID back and sends a dynamic response to the integration.
Sentences can reveal what is happening in a household and where devices are, so Debug output should retain a classification result only — never the original sentence or the device ID. A trigger does not need a separate response node to produce an external response of its own: a fixed response is sent directly, and dynamic mode can send a fallback after a timeout. For observation only, leave the fixed response and fallback blank or set to not respond, and do not wire a response-mode node in at all. Mode being set to trigger does not by itself make a node purely input. If a response ID expires or is lost, end that invocation there — never retry with the old ID.
Fire Event — an output, not a test button
When Fire Event receives a message, it sends fire_event over WebSocket. The event type can come from configuration or from the message, and its data can be built with a template or JSONata. Other Home Assistant automations may be listening for that event type, so even a "just a custom event" can trigger an alarm, a notification, or a device action somewhere else. Keep the node disabled, do not wire an Inject node into it, and do not Deploy it "just to try the button."
Device — check the integration and the capability first
Device offers Trigger and Action modes, and this device-automation path also requires the hass-node-red custom integration to be loaded in Home Assistant. Trigger mode registers through the integration and emits a message when the device's own trigger fires. Action mode, after receiving a Node-RED message, sends the configured action and capabilities to Home Assistant. Available triggers, actions, and capabilities come from the integration that owns the device — they are not a common list this guide can promise applies to every device. One sensor's "single press" is not guaranteed to exist on another brand's equivalent.
If the editor does not show a capability you expected, confirm the device registry entry and the integration's support for it in Home Assistant first — do not hand-write a guessed object. A test flow should carry only a disabled Device Trigger, with Device Action left unwired; for real control, the review in this part's earlier sections on action, target, and data still applies.
Tag — both the tag and the scanner can identify a person
Tag can listen for one tag or for all of them, and it can restrict which scanning devices are accepted. When no devices are specified, this pinned build accepts a scan from any device. Its output can include the tag name, tag ID, device ID, and user ID — fields that can reveal people and locations. Apply the narrowest filter you can, emit only an anonymous purpose code, and never leave "all tags" enabled in a production flow.
Webhook — knowing the ID can be enough to invoke it
Webhook also requires the hass-node-red custom integration. It registers a webhook ID and its permitted POST, PUT, GET, and HEAD methods with that integration, then receives the payload, headers, and params of whatever calls it. It is not a Node-RED HTTP In node, and it does not imply authenticated user access on its own — real reachability depends on how Home Assistant is exposed to the network and on the webhook mechanism itself. Do not expose Home Assistant for convenience, and never put a webhook ID in a tutorial, a screenshot, a repository, or a Debug output.
PLACEHOLDER_WEBHOOK_ID is a non-resolvable marker only. Keep the Webhook node disabled, enter no real value, run no test request against it, and display no external URL for it. If a real requirement is later approved, that calls for its own threat model — method allowlists, source validation, rate limits, and a revocation procedure, worked out separately.Zone — crossing a line is not the same as being there
Zone compares the old and new coordinates of a person or tracked entity against a zone's own coordinates to detect entering, leaving, or both. It produces no output without old_state/new_state, location data, and zone data all present. Location latency, GPS drift, and radius boundaries can all cause flapping, and location data is highly sensitive. A safe flow emits only "anonymous zone state changed," adds a duration threshold or cooldown, and never stores raw coordinates.
Time and Time entity — scheduling, and a writable time
Time reads one property (the default is state) from a specified entity. This guide only vouches for a previously validated date string — ideally ISO 8601 with an offset — or an HH:MM[:SS] time. If an upstream value is a numeric epoch, convert it to an ISO string explicitly before it reaches Time; never pass the raw number in. The node applies a positive or negative offset, can optionally randomize within that offset range, and can repeat daily on selected weekdays. It rebuilds the timer whenever the source entity's state or attribute changes.
| Input condition | Safe interpretation | Response |
|---|---|---|
| A one-time value is already in the past | Must not run immediately as a catch-up | Record an expired status; never treat the value as a catch-up trigger |
| Daily repeat at HH:MM | Uses the runtime environment's local time | Manually verify the next run before and after a DST transition |
| Negative offset plus randomization | May be constrained to avoid landing earlier than now | Do not use for legally regulated or precision-critical timing |
| Entity update or reconnection | The schedule may be recalculated | Build a downstream dedup key from the event date and purpose |
unknown / unavailable | Not a valid time at all | Take the error branch; do not keep the previous value |
DST is not simply an hour added or subtracted. Some local times do not exist on the day of the transition; others happen twice. Test both directions in isolation, using the real time zones of Home Assistant, the Node-RED host, and whatever calendar is the source, and make every downstream action idempotent regardless of which way the clock moved. When you need an absolute time, keep an ISO timestamp with its offset and convert it only for display — never mix it with a time-zone-free string further down the flow.
Time entity is a separate node. It also needs the hass-node-red custom integration, paired with an Entity config, to expose a time value back to Home Assistant. Its modes are listen, get, and set. set is a write: in this pinned build it validates a 24-hour HH:MM[:SS] value and adds seconds automatically if you omit them. listen and get only read the existing value. It is not a replacement for the ordinary WebSocket Time trigger. Keep all three modes disabled while testing, and in particular, never wire set to anything untrusted.
Calendar — polling, not a guarantee
Events: calendar selects one calendar entity and fires at its start or end, adjusted by a positive or negative offset; Filter does a substring match against the event summary. This pinned build polls roughly every 15 minutes, with a one-minute overlap between query windows, then schedules precise timers in an in-memory queue and reduces duplicates with a cache of events it has already emitted. All-day events are aligned to midnight before the offset is applied.
Those mechanisms make ordinary scheduling more reliable — they do not mean "never miss, never duplicate." A calendar entry added or changed at the last minute between two polls may not be caught in time. Restarting Node-RED clears both the in-memory timer queue and the emitted-event cache, and what gets rescanned afterward depends on whatever query window happens to be current when it restarts. Network errors are retried, but if a whole polling round ultimately fails, no events survive it. Your own flow has to define the recovery policy here — this node does not guarantee catch-up delivery on its own.
A safe "meeting reminder" specification
- Keep Calendar disabled and set its entity to
PLACEHOLDER_CALENDAR_ENTITY. Use a filter term that names no person or location. - Output only an anonymized ID. Retain a hashed UID, the event start time, and a classification — never the description, location, or original summary.
- Deduplicate by "anonymous UID + occurrence date + reminder type," and give that key a reasonable expiry rather than keeping schedule details indefinitely.
- Route anything uncertain to a bounded diagnostic Debug, not to a live notification — events past their lateness tolerance, ones that fail time parsing, ones arriving just after a reconnection, or ones missing required data.
- Keep the real notification node disabled until the rate-limit and error-branch checks that Part 7 covers have their own separate approval.
If what you actually need is to wait for a condition rather than for a calendar time, that is Wait Until territory — covered where motion lighting comes up next. Those two questions are genuinely different: "when is the scheduled time" and "when does the condition become true." Do not reach for a long Delay to guess at an external state you could just query.
Disable first, then go read the source of the problem
| Symptom | Likely cause | What to do |
|---|---|---|
Palette shows Action, export says api-call-service | Expected persisted type in this pinned build | Do not hand-edit the JSON type or rebuild the node over its legacy name |
| "The action format is invalid" | The action string is not in domain.service form | Keep the node disabled, confirm the draft still uses PLACEHOLDER_HA_ACTION; any later real value must use the dotted form. Do not guess with a real action to test it |
| The target is broader than expected | Several target types coexist, payload.target merged in because block=false, or area/floor/label membership changed | Abort the change and return to one placeholder target type |
| Configured data was replaced by something else | A flow or global key reached through mergeContext | Clear the contaminated key, inventory what else writes it, and enforce a fixed schema |
No response appears in msg.payload after success | This can be normal | Confirm the action's metadata declares a response, then map it explicitly through Output Properties. Never fabricate a result |
| Stale operations appear after reconnection | Queue is set to something other than none | first, last, and all can all retain stale intent, and all is drained with pop(). Restore none for control Actions |
| Only a timeout is visible; HA's real state is uncertain | The response was lost, not necessarily the action | Do not retry immediately. Verify with an action-specific read-only query, keep a sanitized correlation value, and follow your idempotency and manual-recovery policy |
| Events: all produces a flood | Event Type is blank, or the event data JSON is not actually narrowing anything | Disable the node immediately, clear Debug, and review only the known PLACEHOLDER_EVENT_TYPE |
| Device is missing an expected trigger or action | The integration or device does not expose that capability here | Confirm the HA device registry entry and integration support. Do not copy another device's configuration or guess |
| Time reports invalid, unavailable, or in the past | Wrong property path, type, time zone, offset, or weekday selection | Route to an error branch. Do not reuse an old value, and do not read "ignore past date" as permission to catch up |
| Calendar misses a last-minute change | The change landed inside the roughly-15-minute polling gap, or a query exhausted its retries | If polling latency is genuinely unacceptable for your use case, choose a different architecture rather than shortening an undocumented internal constant |
| Debug or logs expose a real target, tag, device, or webhook ID | A live value slipped into an example, screenshot, or issue report | Disable the flow, delete the Debug message, rotate any exposed webhook ID, mask the affected identifiers, and follow your incident-reporting procedure — never paste the raw value into a public issue while explaining it |
Why does the UI say Action when the JSON type is not action?
api-call-service for compatibility with flows built before the rename. The palette label and the JSON type are allowed to differ; do not edit the JSON type by hand to match the label.Does Block Input Overrides=true stop every dynamic influence on the node?
Can a target use entity, area, and label at the same time?
Is queue: all a reliable offline FIFO?
pop() once the connection is ready — not necessarily in the order the messages arrived. It offers no exactly-once, deadline, or durability guarantee. Use none for control Actions.Does every Action write its return value to msg.payload?
response?.response as a source named results, which still needs an explicit Output Properties mapping to land anywhere. Plan for it being undefined when the action has no response at all.Can I retry an Action immediately after a timeout?
Can I leave Event Type blank in Events: all to explore what is out there?
Why does my Device node lack an option shown in another tutorial?
Can I use the home_assistant_client "connected" event to build a reconnection gate?
states_loaded, services_loaded, running, and ready — the connected handler exists in code but is not wired to fire. Even after using running or ready as a health signal, query the state you need again and validate the timing and dedup key before you act on it.Can Time's daily repeat guarantee exactly one run on a DST transition day?
Can a webhook ID be part of an ordinary, public-facing URL?
Will Calendar deliver every missed event after a restart?
Where to go from here
You know how a call is built, and how a flow gets woken up. Next comes the safe way to wire the two together.
Part 7 takes both halves of this part into two concrete, everyday designs: motion-triggered lighting, built on the entity-state and time-scheduling models from this part plus the Wait Until pattern for a condition rather than a clock; and notifications and climate control, where the Action node's queue and idempotence rules from this part stop being theory and start deciding whether a reminder fires once or five times.
Open the full guidePart 6 of the WoowTech Complete Node-RED Guide series on the Apporo blog.
Adapted from the WoowTech Complete Node-RED Guide, produced by WoowTech and released under CC BY 4.0. This adaptation is published by Apporo under the same licence.
Light · Air · Water · Control · apporo