> For the complete documentation index, see [llms.txt](https://navixy.com/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://navixy.com/docs/iot-logic-api/navixy-iot-guide/merging-external-data-into-a-flow.md).

# Merging external system data into a flow

This guide configures a Data Source node's connector so that data from an external system, one that doesn't use Navixy's native protocols, merges into the stream of a device you already have in your account. The example connects two GPS devices already feeding a flow to a separate battery management system that reports state of charge and temperature for the same vehicles.

This guide walks through the scenario in five steps:

1. [Prerequisites](#prerequisites): confirm which devices already feed the flow, skip this if you already know.
2. [Adding the connector to your Data Source node](#adding-the-connector-to-your-data-source-node).
3. [Getting the push URL](#getting-the-push-url) for that node.
4. [Naming pushed fields](#naming-pushed-fields) so they don't collide with the device's own attributes.
5. [Sending a push](#sending-a-push) and confirming it merged.

For the full field reference, see [Connector configuration](/docs/iot-logic-api/technical-details/nodes.md#connector-configuration).

## Prerequisites

For this example, let's presume that you already have:

1. An API key with access to the IoT Logic API.
2. A flow with ID `42` containing a Data Source node (ID `1`) that lists two GPS devices, `987654` and `987655`, in `source_ids`.

If you don't remember which devices are assigned to that flow, list them:

## flowSourcesList

> Return the device source IDs currently feeding a specific flow. Pass \`flow\_id=0\` to query the default flow — returns all trackers in the account not explicitly assigned to any custom flow. Pass a specific flow ID to get only the device IDs explicitly linked to that flow. Use this to determine which devices a flow processes before modifying source assignments.

```json
{"openapi":"3.1.0","info":{"title":"Navixy IoT Logic API","version":"1.0.0"},"tags":[{"name":"Flow","description":"Operations for creating, reading, updating, and deleting flows. Also covers flow validation, system-provided templates, and device source queries — all resources under the /iot/logic/flow/ path."}],"servers":[{"url":"https://api.eu.navixy.com/v2","description":"Navixy production server on European platform"},{"url":"https://api.us.navixy.com/v2","description":"Navixy production server on American platform"}],"security":[{"api_key":[]}],"components":{"securitySchemes":{"api_key":{"type":"apiKey","description":"Enter an API key with the \"NVX \" prefix, e.g. \"NVX 123456abcdefg\"","name":"Authorization","in":"header"}},"responses":{"ResponseError":{"description":"Error response object","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always false."},"status":{"type":"object","description":"Error status. It only presence when an error occurred.","properties":{"code":{"type":"integer","description":"An error code in this API (not a HTTP code)"},"description":{"type":"string","description":"An error description"}}}}}}}}}},"paths":{"/iot/logic/flow/sources/list":{"get":{"tags":["Flow"],"summary":"flowSourcesList","description":"Return the device source IDs currently feeding a specific flow. Pass `flow_id=0` to query the default flow — returns all trackers in the account not explicitly assigned to any custom flow. Pass a specific flow ID to get only the device IDs explicitly linked to that flow. Use this to determine which devices a flow processes before modifying source assignments.","operationId":"flowSourcesList","parameters":[{"name":"flow_id","in":"query","required":true,"schema":{"type":"integer","minimum":0,"description":"Flow ID. Use 0 to query the default flow."}}],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"`true` if request finished successfully.","readOnly":true},"value":{"type":"array","description":"Device source IDs feeding the flow.","readOnly":true,"items":{"type":"integer"}}}}}}},"default":{"$ref":"#/components/responses/ResponseError"}}}}}}
```

```bash
curl -X GET "https://api.eu.navixy.com/v2/iot/logic/flow/sources/list?flow_id=42" \
  -H "Authorization: NVX your_api_key" \
  -H "Content-Type: application/json"
```

```json
{
  "success": true,
  "value": [987654, 987655]
}
```

## Adding the connector to your Data Source node

Update the flow, adding `push_type`, `primary_key`, and `mappings` to the existing Data Source node. `source_ids` stays as is, the connector only adds a second inbound path for the same devices.

## flowUpdate

> Update an existing flow's configuration. Requires the complete flow object including all nodes and edges. To modify individual nodes without affecting the entire flow, use the endpoint management API. Changes take effect immediately for enabled flows.

```json
{"openapi":"3.1.0","info":{"title":"Navixy IoT Logic API","version":"1.0.0"},"tags":[{"name":"Flow","description":"Operations for creating, reading, updating, and deleting flows. Also covers flow validation, system-provided templates, and device source queries — all resources under the /iot/logic/flow/ path."}],"servers":[{"url":"https://api.eu.navixy.com/v2","description":"Navixy production server on European platform"},{"url":"https://api.us.navixy.com/v2","description":"Navixy production server on American platform"}],"security":[{"api_key":[]}],"components":{"securitySchemes":{"api_key":{"type":"apiKey","description":"Enter an API key with the \"NVX \" prefix, e.g. \"NVX 123456abcdefg\"","name":"Authorization","in":"header"}},"schemas":{"Flow":{"type":"object","description":"Flowchart object","allOf":[{"$ref":"#/components/schemas/FlowId"},{"$ref":"#/components/schemas/FlowDraft"}]},"FlowId":{"type":"object","required":["id"],"properties":{"id":{"type":"integer","description":"Flow ID","readOnly":true}}},"FlowDraft":{"type":"object","description":"Flow object used with the create and update API endpoints. When calling these endpoints, this object must be nested inside a \"flow\" envelope key: {\"flow\": <FlowDraft>}. Includes runtime settings (enabled, default_flow). For a portable export/import format that omits runtime fields and is passed directly without an envelope, see FlowExport.","properties":{"title":{"type":"string","description":"Flow name"},"description":{"type":["string","null"],"description":"Flow description (optional)"},"enabled":{"type":"boolean","description":"Enable/disable flag"},"default_flow":{"type":"boolean","description":"Whether this is the default flow"},"nodes":{"type":"array","items":{"$ref":"#/components/schemas/Node"}},"edges":{"type":"array","items":{"$ref":"#/components/schemas/Edge"}}},"required":["title"]},"Node":{"type":"object","description":"Flowchart Node","oneOf":[{"$ref":"#/components/schemas/NodeDataSource"},{"$ref":"#/components/schemas/NodeInitiateAttributes"},{"$ref":"#/components/schemas/NodeLogic"},{"$ref":"#/components/schemas/NodeAction"},{"$ref":"#/components/schemas/NodeWebhook"},{"$ref":"#/components/schemas/NodeOutputEndpoint"}]},"NodeDataSource":{"type":"object","description":"Node: Data Source","properties":{"id":{"$ref":"#/components/schemas/NodeID"},"type":{"type":"string","description":"Node type. Always \"data_source\"."},"data":{"type":"object","properties":{"title":{"type":"string"},"source_ids":{"type":["array","null"],"items":{"type":"integer","description":"Source ID"},"description":"Array of device source IDs. Use each device's `source.id` from `tracker/list`, not the tracker object ID (`list[].id`). Submitting a tracker object ID is accepted but silently normalized to an empty array, leaving the node with no device attached and returning no error. Use null or an empty array `[]` for no devices. Note: the API normalizes null to `[]` in responses."},"push_type":{"$ref":"#/components/schemas/ExternalDataPushType"},"primary_key":{"type":"string","maxLength":64,"pattern":"^[a-zA-Z0-9_]+$","description":"Name of the field that identifies the target device in an inbound push. Required together with mappings."},"mappings":{"type":["array","null"],"items":{"$ref":"#/components/schemas/DataSourcePushMapping"},"description":"Maps values of the primary_key field to devices already listed in source_ids. Each source_id in this list must also appear in source_ids. Required together with primary_key."}}},"view":{"$ref":"#/components/schemas/NodeView"}},"required":["type","data"]},"NodeID":{"type":"integer","description":"Node ID inside current flow"},"ExternalDataPushType":{"type":"string","description":"Connector type for external data pushes into a Data Source node. Currently only http is available, the enum leaves room for future connector types.","enum":["unknown","http"]},"DataSourcePushMapping":{"type":"object","description":"Maps one inbound primary_key value to a device already listed in the Data Source node's source_ids.","required":["source_id","primary_key_value"],"properties":{"source_id":{"type":"integer","description":"Device ID. Must also appear in the node's source_ids."},"primary_key_value":{"type":"string","maxLength":255,"pattern":"^[a-zA-Z0-9_]+$","description":"Value that identifies this device in an inbound push. Must be unique within the node's mappings."}}},"NodeView":{"type":"object","description":"Flowchart Node view properties","properties":{"position":{"type":"object","description":"Position of the left top corner","properties":{"x":{"type":"integer"},"y":{"type":"integer"}}}}},"NodeInitiateAttributes":{"type":"object","description":"Node: Initiate Attributes. Transforms device data by creating new calculated attributes based on incoming telemetry. Enables data enrichment through mathematical operations, unit conversions, bit-level manipulations, and time-based calculations using Navixy IoT Logic Expression Language (based on JEXL). Developers can consult the official JEXL specification for expression syntax and operators. Calculated attributes become available to all downstream nodes and can be displayed in Data Stream Analyzer or configured as custom sensors in Navixy tracking interface when connected to default output endpoint.","properties":{"id":{"$ref":"#/components/schemas/NodeID"},"type":{"type":"string","description":"Node type. Always \"initiate_attributes\"."},"data":{"type":"object","properties":{"title":{"type":"string","description":"Node title identifying the transformation purpose."},"items":{"type":"array","description":"List of attributes to calculate. Attributes are processed sequentially in the order defined. Each attribute can reference original device parameters and previously calculated attributes within the same node.","items":{"type":"object","properties":{"name":{"type":"string","description":"Attribute identifier. This name appears in Data Stream Analyzer and can be used to create custom sensors in Navixy tracking interface. If attribute name matches existing device parameter, the calculated value replaces the original in output data packets. Uniqueness across the account isn't currently enforced at save time. Reusing a name already defined in another flow is accepted, and the flows silently share that name in Data Stream Analyzer, custom sensor bindings, and output data packets."},"value":{"type":"string","description":"JEXL-based expression to calculate attribute value. Can reference device parameters using direct syntax (e.g., 'temperature') or value() function (e.g., value('temperature', 0, 'valid')). Supports standard JEXL operators and mathematical operations, historical value access (via index parameter), and Navixy-specific bit-level operations through util: namespace functions. Consult JEXL specification for expression syntax details. Expression errors result in null values."},"generation_time":{"type":["string","null"],"description":"JEXL-based expression for device-side generation timestamp (when data was created on device). Defaults to now() if null or not specified. Use genTime() function to reference original parameter timestamps."},"server_time":{"type":["string","null"],"description":"JEXL-based expression for server-side reception timestamp (when data was received by IoT Logic). Defaults to now() if null or not specified. Use srvTime() function to reference original parameter timestamps."}},"required":["name","value"]}}}},"view":{"$ref":"#/components/schemas/NodeView"}},"required":["type","data"]},"NodeLogic":{"type":"object","description":"Node: Logic. Creates conditional branching points that route data based on boolean expressions. Logic nodes evaluate incoming data against user-defined conditions and direct data flow through THEN (true) or ELSE (false) output paths. The node creates a boolean attribute that stores evaluation results and can be used in subsequent nodes or Navixy monitoring systems. Logic nodes support multiple outgoing connections on both THEN and ELSE branches. When a THEN target is a terminal node (Action, Webhook), add a second then_edge from this Logic node directly to the Output Endpoint to ensure data is still forwarded to Navixy, both edges fire in parallel.","properties":{"id":{"$ref":"#/components/schemas/NodeID"},"type":{"type":"string","description":"Node type. Always \"logic\"."},"data":{"type":"object","properties":{"title":{"type":"string"},"name":{"type":"string","description":"Internal identifier for the logic node"},"condition":{"type":"string","description":"Boolean expression evaluated for each incoming message. Must return true or false. Expression can reference attributes from connected upstream nodes using direct attribute syntax or value() function. Supports logical operators (&&, ||, !), comparison operators (<, >, <=, >=, ==, !=), and complex conditions. If expression cannot be evaluated (null values, syntax errors, missing attributes), result is treated as false and data flows through ELSE path."}},"required":["title","name","condition"]},"view":{"$ref":"#/components/schemas/NodeView"}},"required":["type","data"]},"NodeAction":{"description":"Node: Device action. Executes automated device commands when triggered by incoming data. Enables direct device control through output switching and GPRS command transmission. By default, commands are sent to the device that triggered the node. Use device_mapping to redirect commands to other target devices instead. Functions as a terminal node that cannot have outgoing connections.","type":"object","required":["type","data"],"properties":{"id":{"$ref":"#/components/schemas/NodeID"},"type":{"type":"string","description":"Node type. Always \"action\"."},"data":{"type":"object","properties":{"title":{"type":"string"},"trackable_node_id":{"type":"string","readOnly":true,"maxLength":64,"description":"Auto-generated identifier for this node. Read-only; cannot be set through the API. Also appears as this node's attribute name in Data Stream Analyzer."},"actions":{"type":["array","null"],"description":"List of actions to execute sequentially when node is triggered. Actions execute in order from first to last. Maximum 10 actions per node. Commands are sent to the trigger device by default, or to target devices specified in device_mapping.","items":{"oneOf":[{"$ref":"#/components/schemas/SetOutput"},{"$ref":"#/components/schemas/SendCommand"}]},"maxItems":10,"uniqueItems":true},"device_mapping":{"type":["array","null"],"description":"Optional cross-device command routing. Maps source devices to target devices that will receive the actions instead of the source device. If null or empty, actions are sent to the device that triggered the node.","uniqueItems":true,"items":{"$ref":"#/components/schemas/DeviceMapping"}}}},"view":{"$ref":"#/components/schemas/NodeView"}}},"SetOutput":{"type":"object","title":"SetOutput","description":"Switches a device output port to the specified on/off state.","required":["type","number","value"],"properties":{"type":{"type":"string","enum":["set_output"],"description":"Action type. Must be 'set_output'."},"number":{"type":"integer","minimum":1,"maximum":8,"description":"Output number as seen in UI."},"value":{"type":"boolean","description":"The state to set to."}}},"SendCommand":{"type":"object","title":"SendCommand","description":"Sends a custom GPRS command string to the device. Use for device-specific commands as documented by the device manufacturer.","required":["type","command"],"properties":{"type":{"type":"string","enum":["send_gprs_command"],"description":"Action type. Must be 'send_gprs_command'."},"command":{"type":"string","minLength":1,"maxLength":512,"description":"A custom command to send to the device. Consult its documentation for details."},"reliable":{"type":["boolean","null"],"default":true,"description":"Reliable commands will be retried in case if a tracker is not online at the moment."}}},"DeviceMapping":{"type":"object","description":"Maps a source device to one or more target devices for cross-device command routing. When a source device triggers the node, actions are sent to the specified target devices instead.","required":["source_id","target_source_ids"],"properties":{"source_id":{"type":"integer","description":"ID of the source device that triggers the routing rule."},"target_source_ids":{"type":"array","description":"IDs of the target devices that will receive the actions when the source device triggers the node.","minItems":1,"items":{"type":"integer"}}}},"NodeWebhook":{"type":"object","description":"Node: Webhook. Sends HTTP POST requests with IoT data to external endpoints, enabling real-time integration with third-party systems and APIs. Webhook nodes function as terminal nodes that cannot have outgoing connections to other nodes in the flow.","properties":{"id":{"$ref":"#/components/schemas/NodeID"},"type":{"type":"string","description":"Node type. Always \"webhook\"."},"data":{"type":"object","properties":{"title":{"type":"string","description":"Webhook node title.","maxLength":255},"trackable_node_id":{"type":"string","readOnly":true,"maxLength":64,"description":"Auto-generated identifier for this node. Read-only; cannot be set through the API. Also appears as this node's attribute name in Data Stream Analyzer."},"url":{"type":"string","description":"Target URL for HTTP POST requests. Must use http:// or https:// protocol (HTTPS recommended). Executes fire-and-forget POST request on each incoming message without waiting for response or retrying on failure.","maxLength":255,"pattern":"^https?:\\/\\/\\S+$"},"headers":{"type":["array","null"],"description":"Optional HTTP headers for POST requests. All headers including Content-Type must be explicitly specified. Maximum 10 headers.","maxItems":10,"items":{"type":"object","required":["key","value"],"properties":{"key":{"type":"string","description":"Header name.","maxLength":255},"value":{"type":"string","description":"Header value (static only).","maxLength":255}}}},"body":{"type":["string","null"],"description":"Optional request body template. Use \"attribute_name\" syntax to reference attributes from upstream nodes. Supports nested paths (e.g., \"location.latitude\"). Direct attribute references only; expressions not supported. Null attributes send null values.","maxLength":4000}},"required":["title","url"]},"view":{"$ref":"#/components/schemas/NodeView"}},"required":["type","data"]},"NodeOutputEndpoint":{"type":"object","description":"Node: output endpoint. This is the terminating node for any flow. It determines where the messages will be sent.","properties":{"id":{"$ref":"#/components/schemas/NodeID"},"type":{"type":"string","description":"Node type. Always \"output_endpoint\"."},"data":{"type":"object","oneOf":[{"$ref":"#/components/schemas/NodeOutputEndpointDataDefault"},{"$ref":"#/components/schemas/NodeOutputEndpointDataStoredEndpoint"}]},"view":{"$ref":"#/components/schemas/NodeView"}},"required":["type","data"]},"NodeOutputEndpointDataDefault":{"type":"object","description":"Data of default output endpoint (Navixy platform)","properties":{"title":{"type":"string"},"output_endpoint_type":{"type":"string","description":"Type of endpoint. Always 'output_default' for Navixy platform."}},"required":["title","output_endpoint_type"]},"NodeOutputEndpointDataStoredEndpoint":{"type":"object","description":"Data of MQTT output endpoint","properties":{"title":{"type":"string"},"output_endpoint_type":{"type":"string","description":"Type of endpoint. Always 'output_mqtt_client'."},"output_endpoint_id":{"type":"integer","description":"Output Endpoint identifier within the user account. It is required for only some types of node (e.g. output_mqtt_client) which has specific properties."}},"required":["title","output_endpoint_type","output_endpoint_id"]},"Edge":{"type":"object","description":"Edge between two Nodes","properties":{"from":{"type":"integer"},"to":{"type":"integer"},"type":{"description":"Edge type, optional, if not specified it is `simple_edge`","$ref":"#/components/schemas/EdgeType"}},"required":["from","to"]},"EdgeType":{"type":"string","enum":["simple_edge","then_edge","else_edge"]}},"responses":{"EntityCreatedResponse":{"description":"Successful creation response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"`true` if request finished successfully","readOnly":true},"id":{"type":"integer","description":"ID of the created entity","readOnly":true}}}}}},"ResponseError":{"description":"Error response object","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always false."},"status":{"type":"object","description":"Error status. It only presence when an error occurred.","properties":{"code":{"type":"integer","description":"An error code in this API (not a HTTP code)"},"description":{"type":"string","description":"An error description"}}}}}}}}}},"paths":{"/iot/logic/flow/update":{"post":{"tags":["Flow"],"summary":"flowUpdate","description":"Update an existing flow's configuration. Requires the complete flow object including all nodes and edges. To modify individual nodes without affecting the entire flow, use the endpoint management API. Changes take effect immediately for enabled flows.","operationId":"flowUpdate","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"flow":{"description":"The flow definition, wrapped in this \"flow\" key as required by the create/update endpoints. Export/import operations do not use this envelope — they accept the flow object directly.","$ref":"#/components/schemas/Flow"}},"required":["flow"]}}}},"responses":{"200":{"$ref":"#/components/responses/EntityCreatedResponse"},"default":{"$ref":"#/components/responses/ResponseError"}}}}}}
```

```bash
curl -X POST "https://api.eu.navixy.com/v2/iot/logic/flow/update" \
  -H "Authorization: NVX your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "flow": {
      "id": 42,
      "title": "Fleet vehicles with battery management enrichment",
      "enabled": true,
      "nodes": [
        {
          "id": 1,
          "type": "data_source",
          "data": {
            "title": "Fleet vehicles",
            "source_ids": [987654, 987655],
            "push_type": "http",
            "primary_key": "vehicle_id",
            "mappings": [
              { "source_id": 987654, "primary_key_value": "truck_12" },
              { "source_id": 987655, "primary_key_value": "truck_07" }
            ]
          },
          "view": { "position": { "x": 50, "y": 50 } }
        },
        {
          "id": 2,
          "type": "output_endpoint",
          "data": {
            "title": "Send to Navixy",
            "output_endpoint_type": "output_default"
          },
          "view": { "position": { "x": 250, "y": 50 } }
        }
      ],
      "edges": [
        { "from": 1, "to": 2 }
      ]
    }
  }'
```

```json
{
  "success": true,
  "id": 42
}
```

## Getting the push URL

No endpoint returns this URL directly, not `flow/read` or any other. Assemble it yourself from two pieces: the flow ID and node ID, and a domain to reach the push endpoint through.

### Flow ID and node ID

You already have these from the steps above: flow `42`, Data Source node `1`. If you need to look them up again later, `flow/read` returns them as `value.id` and `value.nodes[].id`, it just never returns a `url` field alongside them:

## flowRead

> Retrieve complete flow configuration including all nodes, edges, and metadata. Returns the flow structure with node positions, connections, and enabled status. Use this endpoint to inspect flow architecture or retrieve configuration for duplication.

```json
{"openapi":"3.1.0","info":{"title":"Navixy IoT Logic API","version":"1.0.0"},"tags":[{"name":"Flow","description":"Operations for creating, reading, updating, and deleting flows. Also covers flow validation, system-provided templates, and device source queries — all resources under the /iot/logic/flow/ path."}],"servers":[{"url":"https://api.eu.navixy.com/v2","description":"Navixy production server on European platform"},{"url":"https://api.us.navixy.com/v2","description":"Navixy production server on American platform"}],"security":[{"api_key":[]}],"components":{"securitySchemes":{"api_key":{"type":"apiKey","description":"Enter an API key with the \"NVX \" prefix, e.g. \"NVX 123456abcdefg\"","name":"Authorization","in":"header"}},"schemas":{"Flow":{"type":"object","description":"Flowchart object","allOf":[{"$ref":"#/components/schemas/FlowId"},{"$ref":"#/components/schemas/FlowDraft"}]},"FlowId":{"type":"object","required":["id"],"properties":{"id":{"type":"integer","description":"Flow ID","readOnly":true}}},"FlowDraft":{"type":"object","description":"Flow object used with the create and update API endpoints. When calling these endpoints, this object must be nested inside a \"flow\" envelope key: {\"flow\": <FlowDraft>}. Includes runtime settings (enabled, default_flow). For a portable export/import format that omits runtime fields and is passed directly without an envelope, see FlowExport.","properties":{"title":{"type":"string","description":"Flow name"},"description":{"type":["string","null"],"description":"Flow description (optional)"},"enabled":{"type":"boolean","description":"Enable/disable flag"},"default_flow":{"type":"boolean","description":"Whether this is the default flow"},"nodes":{"type":"array","items":{"$ref":"#/components/schemas/Node"}},"edges":{"type":"array","items":{"$ref":"#/components/schemas/Edge"}}},"required":["title"]},"Node":{"type":"object","description":"Flowchart Node","oneOf":[{"$ref":"#/components/schemas/NodeDataSource"},{"$ref":"#/components/schemas/NodeInitiateAttributes"},{"$ref":"#/components/schemas/NodeLogic"},{"$ref":"#/components/schemas/NodeAction"},{"$ref":"#/components/schemas/NodeWebhook"},{"$ref":"#/components/schemas/NodeOutputEndpoint"}]},"NodeDataSource":{"type":"object","description":"Node: Data Source","properties":{"id":{"$ref":"#/components/schemas/NodeID"},"type":{"type":"string","description":"Node type. Always \"data_source\"."},"data":{"type":"object","properties":{"title":{"type":"string"},"source_ids":{"type":["array","null"],"items":{"type":"integer","description":"Source ID"},"description":"Array of device source IDs. Use each device's `source.id` from `tracker/list`, not the tracker object ID (`list[].id`). Submitting a tracker object ID is accepted but silently normalized to an empty array, leaving the node with no device attached and returning no error. Use null or an empty array `[]` for no devices. Note: the API normalizes null to `[]` in responses."},"push_type":{"$ref":"#/components/schemas/ExternalDataPushType"},"primary_key":{"type":"string","maxLength":64,"pattern":"^[a-zA-Z0-9_]+$","description":"Name of the field that identifies the target device in an inbound push. Required together with mappings."},"mappings":{"type":["array","null"],"items":{"$ref":"#/components/schemas/DataSourcePushMapping"},"description":"Maps values of the primary_key field to devices already listed in source_ids. Each source_id in this list must also appear in source_ids. Required together with primary_key."}}},"view":{"$ref":"#/components/schemas/NodeView"}},"required":["type","data"]},"NodeID":{"type":"integer","description":"Node ID inside current flow"},"ExternalDataPushType":{"type":"string","description":"Connector type for external data pushes into a Data Source node. Currently only http is available, the enum leaves room for future connector types.","enum":["unknown","http"]},"DataSourcePushMapping":{"type":"object","description":"Maps one inbound primary_key value to a device already listed in the Data Source node's source_ids.","required":["source_id","primary_key_value"],"properties":{"source_id":{"type":"integer","description":"Device ID. Must also appear in the node's source_ids."},"primary_key_value":{"type":"string","maxLength":255,"pattern":"^[a-zA-Z0-9_]+$","description":"Value that identifies this device in an inbound push. Must be unique within the node's mappings."}}},"NodeView":{"type":"object","description":"Flowchart Node view properties","properties":{"position":{"type":"object","description":"Position of the left top corner","properties":{"x":{"type":"integer"},"y":{"type":"integer"}}}}},"NodeInitiateAttributes":{"type":"object","description":"Node: Initiate Attributes. Transforms device data by creating new calculated attributes based on incoming telemetry. Enables data enrichment through mathematical operations, unit conversions, bit-level manipulations, and time-based calculations using Navixy IoT Logic Expression Language (based on JEXL). Developers can consult the official JEXL specification for expression syntax and operators. Calculated attributes become available to all downstream nodes and can be displayed in Data Stream Analyzer or configured as custom sensors in Navixy tracking interface when connected to default output endpoint.","properties":{"id":{"$ref":"#/components/schemas/NodeID"},"type":{"type":"string","description":"Node type. Always \"initiate_attributes\"."},"data":{"type":"object","properties":{"title":{"type":"string","description":"Node title identifying the transformation purpose."},"items":{"type":"array","description":"List of attributes to calculate. Attributes are processed sequentially in the order defined. Each attribute can reference original device parameters and previously calculated attributes within the same node.","items":{"type":"object","properties":{"name":{"type":"string","description":"Attribute identifier. This name appears in Data Stream Analyzer and can be used to create custom sensors in Navixy tracking interface. If attribute name matches existing device parameter, the calculated value replaces the original in output data packets. Uniqueness across the account isn't currently enforced at save time. Reusing a name already defined in another flow is accepted, and the flows silently share that name in Data Stream Analyzer, custom sensor bindings, and output data packets."},"value":{"type":"string","description":"JEXL-based expression to calculate attribute value. Can reference device parameters using direct syntax (e.g., 'temperature') or value() function (e.g., value('temperature', 0, 'valid')). Supports standard JEXL operators and mathematical operations, historical value access (via index parameter), and Navixy-specific bit-level operations through util: namespace functions. Consult JEXL specification for expression syntax details. Expression errors result in null values."},"generation_time":{"type":["string","null"],"description":"JEXL-based expression for device-side generation timestamp (when data was created on device). Defaults to now() if null or not specified. Use genTime() function to reference original parameter timestamps."},"server_time":{"type":["string","null"],"description":"JEXL-based expression for server-side reception timestamp (when data was received by IoT Logic). Defaults to now() if null or not specified. Use srvTime() function to reference original parameter timestamps."}},"required":["name","value"]}}}},"view":{"$ref":"#/components/schemas/NodeView"}},"required":["type","data"]},"NodeLogic":{"type":"object","description":"Node: Logic. Creates conditional branching points that route data based on boolean expressions. Logic nodes evaluate incoming data against user-defined conditions and direct data flow through THEN (true) or ELSE (false) output paths. The node creates a boolean attribute that stores evaluation results and can be used in subsequent nodes or Navixy monitoring systems. Logic nodes support multiple outgoing connections on both THEN and ELSE branches. When a THEN target is a terminal node (Action, Webhook), add a second then_edge from this Logic node directly to the Output Endpoint to ensure data is still forwarded to Navixy, both edges fire in parallel.","properties":{"id":{"$ref":"#/components/schemas/NodeID"},"type":{"type":"string","description":"Node type. Always \"logic\"."},"data":{"type":"object","properties":{"title":{"type":"string"},"name":{"type":"string","description":"Internal identifier for the logic node"},"condition":{"type":"string","description":"Boolean expression evaluated for each incoming message. Must return true or false. Expression can reference attributes from connected upstream nodes using direct attribute syntax or value() function. Supports logical operators (&&, ||, !), comparison operators (<, >, <=, >=, ==, !=), and complex conditions. If expression cannot be evaluated (null values, syntax errors, missing attributes), result is treated as false and data flows through ELSE path."}},"required":["title","name","condition"]},"view":{"$ref":"#/components/schemas/NodeView"}},"required":["type","data"]},"NodeAction":{"description":"Node: Device action. Executes automated device commands when triggered by incoming data. Enables direct device control through output switching and GPRS command transmission. By default, commands are sent to the device that triggered the node. Use device_mapping to redirect commands to other target devices instead. Functions as a terminal node that cannot have outgoing connections.","type":"object","required":["type","data"],"properties":{"id":{"$ref":"#/components/schemas/NodeID"},"type":{"type":"string","description":"Node type. Always \"action\"."},"data":{"type":"object","properties":{"title":{"type":"string"},"trackable_node_id":{"type":"string","readOnly":true,"maxLength":64,"description":"Auto-generated identifier for this node. Read-only; cannot be set through the API. Also appears as this node's attribute name in Data Stream Analyzer."},"actions":{"type":["array","null"],"description":"List of actions to execute sequentially when node is triggered. Actions execute in order from first to last. Maximum 10 actions per node. Commands are sent to the trigger device by default, or to target devices specified in device_mapping.","items":{"oneOf":[{"$ref":"#/components/schemas/SetOutput"},{"$ref":"#/components/schemas/SendCommand"}]},"maxItems":10,"uniqueItems":true},"device_mapping":{"type":["array","null"],"description":"Optional cross-device command routing. Maps source devices to target devices that will receive the actions instead of the source device. If null or empty, actions are sent to the device that triggered the node.","uniqueItems":true,"items":{"$ref":"#/components/schemas/DeviceMapping"}}}},"view":{"$ref":"#/components/schemas/NodeView"}}},"SetOutput":{"type":"object","title":"SetOutput","description":"Switches a device output port to the specified on/off state.","required":["type","number","value"],"properties":{"type":{"type":"string","enum":["set_output"],"description":"Action type. Must be 'set_output'."},"number":{"type":"integer","minimum":1,"maximum":8,"description":"Output number as seen in UI."},"value":{"type":"boolean","description":"The state to set to."}}},"SendCommand":{"type":"object","title":"SendCommand","description":"Sends a custom GPRS command string to the device. Use for device-specific commands as documented by the device manufacturer.","required":["type","command"],"properties":{"type":{"type":"string","enum":["send_gprs_command"],"description":"Action type. Must be 'send_gprs_command'."},"command":{"type":"string","minLength":1,"maxLength":512,"description":"A custom command to send to the device. Consult its documentation for details."},"reliable":{"type":["boolean","null"],"default":true,"description":"Reliable commands will be retried in case if a tracker is not online at the moment."}}},"DeviceMapping":{"type":"object","description":"Maps a source device to one or more target devices for cross-device command routing. When a source device triggers the node, actions are sent to the specified target devices instead.","required":["source_id","target_source_ids"],"properties":{"source_id":{"type":"integer","description":"ID of the source device that triggers the routing rule."},"target_source_ids":{"type":"array","description":"IDs of the target devices that will receive the actions when the source device triggers the node.","minItems":1,"items":{"type":"integer"}}}},"NodeWebhook":{"type":"object","description":"Node: Webhook. Sends HTTP POST requests with IoT data to external endpoints, enabling real-time integration with third-party systems and APIs. Webhook nodes function as terminal nodes that cannot have outgoing connections to other nodes in the flow.","properties":{"id":{"$ref":"#/components/schemas/NodeID"},"type":{"type":"string","description":"Node type. Always \"webhook\"."},"data":{"type":"object","properties":{"title":{"type":"string","description":"Webhook node title.","maxLength":255},"trackable_node_id":{"type":"string","readOnly":true,"maxLength":64,"description":"Auto-generated identifier for this node. Read-only; cannot be set through the API. Also appears as this node's attribute name in Data Stream Analyzer."},"url":{"type":"string","description":"Target URL for HTTP POST requests. Must use http:// or https:// protocol (HTTPS recommended). Executes fire-and-forget POST request on each incoming message without waiting for response or retrying on failure.","maxLength":255,"pattern":"^https?:\\/\\/\\S+$"},"headers":{"type":["array","null"],"description":"Optional HTTP headers for POST requests. All headers including Content-Type must be explicitly specified. Maximum 10 headers.","maxItems":10,"items":{"type":"object","required":["key","value"],"properties":{"key":{"type":"string","description":"Header name.","maxLength":255},"value":{"type":"string","description":"Header value (static only).","maxLength":255}}}},"body":{"type":["string","null"],"description":"Optional request body template. Use \"attribute_name\" syntax to reference attributes from upstream nodes. Supports nested paths (e.g., \"location.latitude\"). Direct attribute references only; expressions not supported. Null attributes send null values.","maxLength":4000}},"required":["title","url"]},"view":{"$ref":"#/components/schemas/NodeView"}},"required":["type","data"]},"NodeOutputEndpoint":{"type":"object","description":"Node: output endpoint. This is the terminating node for any flow. It determines where the messages will be sent.","properties":{"id":{"$ref":"#/components/schemas/NodeID"},"type":{"type":"string","description":"Node type. Always \"output_endpoint\"."},"data":{"type":"object","oneOf":[{"$ref":"#/components/schemas/NodeOutputEndpointDataDefault"},{"$ref":"#/components/schemas/NodeOutputEndpointDataStoredEndpoint"}]},"view":{"$ref":"#/components/schemas/NodeView"}},"required":["type","data"]},"NodeOutputEndpointDataDefault":{"type":"object","description":"Data of default output endpoint (Navixy platform)","properties":{"title":{"type":"string"},"output_endpoint_type":{"type":"string","description":"Type of endpoint. Always 'output_default' for Navixy platform."}},"required":["title","output_endpoint_type"]},"NodeOutputEndpointDataStoredEndpoint":{"type":"object","description":"Data of MQTT output endpoint","properties":{"title":{"type":"string"},"output_endpoint_type":{"type":"string","description":"Type of endpoint. Always 'output_mqtt_client'."},"output_endpoint_id":{"type":"integer","description":"Output Endpoint identifier within the user account. It is required for only some types of node (e.g. output_mqtt_client) which has specific properties."}},"required":["title","output_endpoint_type","output_endpoint_id"]},"Edge":{"type":"object","description":"Edge between two Nodes","properties":{"from":{"type":"integer"},"to":{"type":"integer"},"type":{"description":"Edge type, optional, if not specified it is `simple_edge`","$ref":"#/components/schemas/EdgeType"}},"required":["from","to"]},"EdgeType":{"type":"string","enum":["simple_edge","then_edge","else_edge"]}},"responses":{"ResponseError":{"description":"Error response object","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always false."},"status":{"type":"object","description":"Error status. It only presence when an error occurred.","properties":{"code":{"type":"integer","description":"An error code in this API (not a HTTP code)"},"description":{"type":"string","description":"An error description"}}}}}}}}}},"paths":{"/iot/logic/flow/read":{"get":{"tags":["Flow"],"summary":"flowRead","description":"Retrieve complete flow configuration including all nodes, edges, and metadata. Returns the flow structure with node positions, connections, and enabled status. Use this endpoint to inspect flow architecture or retrieve configuration for duplication.","operationId":"flowRead","parameters":[{"name":"flow_id","in":"query","required":true,"schema":{"type":"integer","description":"Flow ID"}}],"responses":{"200":{"description":"Successful response to read a flow","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"`true` if request finished successfully.","readOnly":true},"value":{"$ref":"#/components/schemas/Flow"}}}}}},"default":{"$ref":"#/components/responses/ResponseError"}}}}}}
```

```bash
curl -X GET "https://api.eu.navixy.com/v2/iot/logic/flow/read?flow_id=42" \
  -H "Authorization: NVX your_api_key" \
  -H "Content-Type: application/json"
```

```json
{
  "success": true,
  "value": {
    "id": 42,
    "title": "Fleet vehicles with battery management enrichment",
    "enabled": true,
    "nodes": [
      {
        "id": 1,
        "type": "data_source",
        "data": {
          "title": "Fleet vehicles",
          "source_ids": [987654, 987655],
          "push_type": "http",
          "primary_key": "vehicle_id",
          "mappings": [
            { "source_id": 987654, "primary_key_value": "truck_12" },
            { "source_id": 987655, "primary_key_value": "truck_07" }
          ]
        },
        "view": { "position": { "x": 50, "y": 50 } }
      },
      {
        "id": 2,
        "type": "output_endpoint",
        "data": {
          "title": "Send to Navixy",
          "output_endpoint_type": "output_default"
        },
        "view": { "position": { "x": 250, "y": 50 } }
      }
    ],
    "edges": [
      { "from": 1, "to": 2 }
    ]
  }
}
```

### Domain: regional API server (recommended)

Reuse the same regional domain every other call in this guide already goes through, `api.eu.navixy.com` or `api.us.navixy.com`. No extra request needed:

`https://api.eu.navixy.com/v2/iot/logic/flow/push?flow_id=<flow_id>&node_id=<node_id>` (or `api.us.navixy.com` on the American platform)

For this example: `https://api.eu.navixy.com/v2/iot/logic/flow/push?flow_id=42&node_id=1`.

### Domain: account web application (alternative)

If you'd rather match the URL the flow builder UI shows on the node's Software tab, use your account's own web application domain instead, for example `demo.navixy.com`, or a custom domain if your organization configured one. This needs one more call: `user/get_info` on the [User API](https://navixy.com/docs/navixy-api/user-api/backend-api/resources/commons/user/index#get_info), same API key as everything else in this guide:

```bash
curl -X GET "https://api.eu.navixy.com/v2/user/get_info" \
  -H "Authorization: NVX your_api_key"
```

```json
{
  "success": true,
  "paas_settings": {
    "domain": "demo.navixy.com"
  }
}
```

`paas_settings.domain` is your account's web application domain. Note the path prefix is `/api-v2/` here, not `/v2/`:

`https://<paas_settings.domain>/api-v2/iot/logic/flow/push?flow_id=<flow_id>&node_id=<node_id>`

For this example: `https://demo.navixy.com/api-v2/iot/logic/flow/push?flow_id=42&node_id=1`.

Give either URL to the battery management system, or whatever external system will send the data.

## Naming pushed fields

Before sending real pushes, pick names for the fields you'll push, `battery_soc` and `battery_temp` in this example. Make sure these names won't collide with the mapped device's own native attributes, or with any other flow's connector enriching the same device.

A collision doesn't fail the push. It silently overwrites the colliding attribute's existing history instead of creating a new one. Downstream nodes, sensors, reports, and alerts then treat the overwritten value as a real reading.

Prefix pushed field names with something specific to the external system, for example `bms_battery_soc`. Alternatively, prefix with the flow's title instead, for example `<flow_title>_battery_soc`. Avoid generic names like `battery_soc`, `fuel_level`, or `board_voltage` that a device might already report natively.

## Sending a push

The URL you built above already carries `flow_id` and `node_id` as query parameters, an external system posting to it doesn't need to repeat them in the request body. If you're sending the request yourself rather than handing the URL to another system, `flow_id` and `node_id` work equally well in the JSON body instead, as shown here, both conventions reach the same endpoint.

## flowPush

> Send data from an external system into a connector-configured Data Source node. Navixy reads the field name stored in the node's primary\_key setting, looks for a field with that name in the request body, and matches its value against the node's mappings. On a match, Navixy merges the remaining fields into the data stream of the mapped device, as additional attributes rather than a location update. flow\_id, node\_id, and the primary-key field are excluded from this merge. A pushed field name can match an attribute the device already has, native or from another flow's connector. When it does, the push overwrites that attribute's existing history instead of creating a new one. See \[Connector configuration]\(<https://www.navixy.com/docs/iot-logic-api/technical-details/nodes#connector-configuration>) for naming guidance. If no field matches, or the request contains no fields besides flow\_id, node\_id, and the primary key, Navixy discards the push without returning an error. Rate limited to 1 request per second per API key, with a burst size of 1.

```json
{"openapi":"3.1.0","info":{"title":"Navixy IoT Logic API","version":"1.0.0"},"tags":[{"name":"Flow","description":"Operations for creating, reading, updating, and deleting flows. Also covers flow validation, system-provided templates, and device source queries — all resources under the /iot/logic/flow/ path."}],"servers":[{"url":"https://api.eu.navixy.com/v2","description":"Navixy production server on European platform"},{"url":"https://api.us.navixy.com/v2","description":"Navixy production server on American platform"}],"security":[{"api_key":[]}],"components":{"securitySchemes":{"api_key":{"type":"apiKey","description":"Enter an API key with the \"NVX \" prefix, e.g. \"NVX 123456abcdefg\"","name":"Authorization","in":"header"}},"responses":{"OK":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"`true` if request finished successfully.","readOnly":true}}}}}},"ResponseError":{"description":"Error response object","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","description":"Always false."},"status":{"type":"object","description":"Error status. It only presence when an error occurred.","properties":{"code":{"type":"integer","description":"An error code in this API (not a HTTP code)"},"description":{"type":"string","description":"An error description"}}}}}}}}}},"paths":{"/iot/logic/flow/push":{"post":{"tags":["Flow"],"summary":"flowPush","description":"Send data from an external system into a connector-configured Data Source node. Navixy reads the field name stored in the node's primary_key setting, looks for a field with that name in the request body, and matches its value against the node's mappings. On a match, Navixy merges the remaining fields into the data stream of the mapped device, as additional attributes rather than a location update. flow_id, node_id, and the primary-key field are excluded from this merge. A pushed field name can match an attribute the device already has, native or from another flow's connector. When it does, the push overwrites that attribute's existing history instead of creating a new one. See [Connector configuration](https://www.navixy.com/docs/iot-logic-api/technical-details/nodes#connector-configuration) for naming guidance. If no field matches, or the request contains no fields besides flow_id, node_id, and the primary key, Navixy discards the push without returning an error. Rate limited to 1 request per second per API key, with a burst size of 1.","operationId":"flowPush","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["flow_id","node_id"],"properties":{"flow_id":{"type":"integer","description":"Flow ID"},"node_id":{"type":"integer","description":"ID of the connector-configured Data Source node inside the flow"}},"additionalProperties":{"type":["string","number","boolean","null"],"description":"Additional fields to merge into the mapped device's data stream. flow_id, node_id, and the primary-key field itself are excluded. Only the remaining fields become attributes. One field must have the name configured in the node's primary_key setting, with a value matching one of the node's mappings. Field names match ^[a-zA-Z0-9_-]+$ and are limited to 64 characters. String values match ^[a-zA-Z0-9_-]*$ and are limited to 100 characters. Nested objects and arrays are ignored. A field name can match an attribute the device already has, native or pushed by another flow's connector. When it does, it overwrites that attribute's existing history instead of creating a new one. Exception: the primary-key value must match the stricter ^[a-zA-Z0-9_]+$ (no hyphens, 1-255 characters) required by `mappings[].primary_key_value`, or it silently fails to match. See [Connector configuration](https://www.navixy.com/docs/iot-logic-api/technical-details/nodes#connector-configuration)."}}}}},"responses":{"200":{"$ref":"#/components/responses/OK"},"default":{"$ref":"#/components/responses/ResponseError"}}}}}}
```

```bash
curl -X POST "https://api.eu.navixy.com/v2/iot/logic/flow/push" \
  -H "Authorization: NVX your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "flow_id": 42,
    "node_id": 1,
    "vehicle_id": "truck_12",
    "battery_soc": 76,
    "battery_temp": 34.2
  }'
```

```json
{
  "success": true
}
```

{% hint style="warning" %}
A `200` response only confirms that Navixy accepted the request. If `vehicle_id` doesn't match any `mappings[].primary_key_value` on the node, Navixy discards the push without returning an error. Check the mapped device's attributes to confirm the data arrived, for example with [WebSocket access to Data Stream Analyzer](/docs/iot-logic-api/websocket-access-for-dsa.md).
{% endhint %}

{% hint style="success" %}
**Congratulations!**

You've now merged an external system's data into an existing device's stream. Device `987654` receives `battery_soc` and `battery_temp` as new attributes, alongside its native GPS telemetry, since neither name was already in use on this device. Any downstream node in the flow can reference them like any other attribute.
{% endhint %}

## Handling errors

Navixy validates the connector fields when you save a flow, on `flowCreate` and `flowUpdate`, not when you send a push. A rejected request returns HTTP 400 with a `292` internal code (`IoT Flow Invalid`) and a validation error for the affected node.

| Error message                                                                             | Cause and resolution                                                                                                           |
| ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `Data source node ${node} requires a primary key to accept pushes`                        | `mappings` has entries but `primary_key` is blank. Set `primary_key` to the field name the external system will send.          |
| `Data source node ${node} requires at least one device mapping to accept pushes`          | `primary_key` is set but `mappings` is empty. Add at least one mapping, or clear `primary_key` for a devices-only node.        |
| `Data source node ${node} maps the same device more than once: ${source_id}`              | The same `source_id` appears in more than one mapping on this node. Each device needs exactly one mapping.                     |
| `Data source node ${node} contains a duplicate primary key value: "${primary_key_value}"` | The same `primary_key_value` appears in more than one mapping on this node. Each mapping value must be unique within the node. |
| `Data source node ${node} maps devices that are not in its device list: ${source_ids}`    | A mapping points at a device that isn't in this node's `source_ids`. Add the device to `source_ids`, or remove the mapping.    |

`${node}` is replaced with the node's title and ID, for example `"Fleet vehicles" (#1)`. `${source_id}`, `${source_ids}`, and `${primary_key_value}` are replaced with the values that caused the error.

### How to handle a rejected flow update

```json
{
  "success": false,
  "status": {
    "code": 292,
    "description": "IoT Flow Invalid"
  },
  "errors": [
    {
      "node_ids": [1],
      "message": "Data source node \"Fleet vehicles\" (#1) maps devices that are not in its device list: #991023"
    }
  ]
}
```

Fix the reported node's connector fields and resend the request.

## See also

* [Connector configuration](/docs/iot-logic-api/technical-details/nodes.md#connector-configuration): full field reference for the connector
* [API reference](/docs/iot-logic-api/resources/api-reference.md): the complete OpenAPI specification
* [WebSocket access to Data Stream Analyzer](/docs/iot-logic-api/websocket-access-for-dsa.md): verify that pushed data reached the mapped device


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://navixy.com/docs/iot-logic-api/navixy-iot-guide/merging-external-data-into-a-flow.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
