Skip to content

Transactional — Automation Node

Nodes are the steps of an automation. Each node has a type, a child (the next node, or null when the contact leaves the flow) and, for ifElse and externalEvent, a child2 (the second branch). A node's settings are kept in payload and are set with a PUT specific to its type.

Endpoints

Method URL
POST /transactional/<transactional>/automation/<automation>/node
PUT /transactional/<transactional>/automation/<automation>/node/<node>/<type>
DELETE /transactional/<transactional>/automation/<automation>/node/<node>

Nodes are read with GET /transactional/<transactional>/automation/<automation>. Send the parameters form-encoded or in the query string.

Node types

Type Kind What it does Branches
contactSignup Entry A contact is added to the list (API, CSV upload, form, or a contact created by an event) child
contactAnniversary Entry The date in a custom field of the contact comes round again (checked every hour) child
externalEventEntry Entry You call the event URL for the contact child
smsReceivedEntry Entry The contact sends an SMS to one of your inbound numbers child
existingContact Entry Not processed at the moment. Do not use. child
loopingContact Entry Not processed at the moment. Do not use. child
sendMessage Action Sends a transactional message (email and/or SMS) child
setField Action Sets custom fields of the contact child
httpCallback Action Sends an HTTP POST to your service child
wait Wait Waits a fixed period child
externalEvent Wait Waits for an event for the contact, with an optional timeout child = event, child2 = timeout
smsReceived Wait Waits for an SMS from the contact, with an optional timeout child (SMS or timeout)
ifElse Condition Tests fields of the contact child = true, child2 = false

POST /transactional/<transactional>/automation/<automation>/node

Creates a node and links it into the flow.

Parameter Type Description
type string Required. One of the node types above
connectFrom string Required. Where to attach the node: null for an entry node; the ID of the parent node (the new node becomes its child); <id>[1] for the second branch (child2) of an ifElse or externalEvent node; or start for the first step after the entry nodes
connectTo string Required. null to add the node at the end of a branch, or the ID of an existing node to insert the new node before it. In that case, every node that pointed to it now points to the new node.
title string Optional. Defaults to a label for the type
comment string Optional. Defaults to a description for the type
  • Entry types (contactSignup, contactAnniversary, externalEventEntry, smsReceivedEntry) must be created with connectFrom=null&connectTo=null, and the other types must not. Otherwise the request fails with Incompatible type and position.
  • A new entry node leads to the same first step as the existing entry nodes.
  • To add a node between two nodes, set connectTo to the next node. With connectTo=null, attach only to a node or branch that has no next node yet.

Returns: The new node (transactionalAutomationNode) and the automation.

Example — add a sendMessage node after node 31:

curl -u "username:api_key" -H "Accept: application/json" \
  -d "type=sendMessage&connectFrom=31&connectTo=null" \
  https://app.duo.pt/transactional/{transactional_id}/automation/{automation_id}/node

Response:

{
  "success": true,
  "data": {
    "transactionalAutomation": {
      "id": 7,
      "name": "Payment follow-up",
      "enabled": false,
      "send_transactional": 1
    },
    "transactionalAutomationNode": {
      "id": 32,
      "send_transactional_automation": 7,
      "type": "sendMessage",
      "title": "Receives a message",
      "comment": "Click to select message",
      "child": null,
      "child2": null,
      "payload": null
    }
  }
}

PUT /transactional/<transactional>/automation/<automation>/node/<node>/<type>

Sets the node's settings. <type> must be the node's type. Send a POST with x-verb=PUT. Each call replaces the whole payload.

Returns: transactional, transactionalAutomation and the updated transactionalAutomationNode, with its payload and a new comment.

contactSignup

Parameter Type Description
segment int Optional. ID of a segment of the list. Stored, but not applied at the moment: every new contact enters.

Payload: {"segment": 5}

contactAnniversary

Parameter Type Description
anniversary_field int ID of the list's custom field that holds the date
anniversary_period string Optional. year (default), month, week, day or hour

Payload: {"field_to_check": 42, "check_period": "year"}

externalEventEntry

Parameter Type Description
event string Required. Event name. It is a label: the event URL identifies the node.
fields_to_set[argument][] string array Required, may be empty. Reserved: stored, but not applied at the moment.
fields_to_set[custom_field][] string array Required, may be empty. Reserved, same index as argument.

Payload: {"event": "payment", "fields_to_set": []}

externalEvent

The parameters of externalEventEntry, plus:

Parameter Type Description
period int Timeout in seconds. On timeout the contact goes to child2. 0 waits for the event only.

Set a timeout only when you need the second branch.

wait

Parameter Type Description
period int Wait in seconds. Must be greater than 0: with 0 the contact stays in the node.

Payload: {"period": 86400}

sendMessage

Parameter Type Description
message int ID of a message of this transactional object. Other IDs are ignored.

Payload: {"message": 12}. The contact receives the message's email if it has an email address, and its SMS if it has a telephone number. Contacts that are not subscribed or are bouncing receive nothing but continue in the flow.

setField

Parameter Type Description
fields_to_set[argument][] string array Required. Values to set
fields_to_set[custom_field][] int array Required. Custom field IDs of the list, same index as the values

Payload: {"fields_to_set": {"VIP": "43"}} (value → field ID). Each value can be used once per node: to write the same value in two fields, use two setField nodes.

ifElse

Parameter Type Description
payload[topoperator] string AND (all conditions) or OR (any condition). Any other value means OR.
payload[condition_field][] string array email, telephone or the ID of a custom field of the list
payload[condition_operator][] string array ==, !=, contains, !contains, starts_with, !starts_with, ends_with, !ends_with; > and < for numbers; before, after and is_today for dates (YYYY-MM-DD or YYYY-MM-DD HH:MM:SS)
payload[condition_operand][] string array Value to compare with (empty for is_today)

Payload: {"topoperator": "AND", "operands": [{"field": "45", "operator": "==", "operand": "yes"}]}. True → child, false → child2.

Tip

Send only complete conditions, with fields of the transactional object's list, and no empty rows. For text fields, prefer == and !=. Check every condition with a test contact before you enable the automation.

httpCallback

Parameter Type Description
url string Required. http:// or https:// URL of your service
parameterName[] string array Names of the body fields
parameterSource[] string array static (fixed value) or field (value of the contact)
parameterStaticValue[] string array The value, when the source is static
parameterFieldValue[] string array email, telephone or a custom field ID, when the source is field
http_error int Optional. ID of a custom field that receives the HTTP status code. When set, the contact also continues to child after an error.
http_result int Optional. ID of a custom field that receives the response body

Send the four parameter…[] arrays with one entry per parameter, using empty values where they do not apply. A parameter with an empty name is ignored.

Payload: {"url": "https://example.com/duo/hook", "http_error": null, "http_result": 44, "http_parameters": [{"name": "email", "source": "field", "fieldValue": "email"}, {"name": "token", "source": "static", "staticValue": "s3cr3t"}]}

The request DUO makes: POST <url> with Content-Type: application/x-www-form-urlencoded and the parameters as body fields (no body if there are none), User-Agent: duo/<version>, and a 60-second timeout. A 2xx answer moves the contact to child. On any other answer, or if the request fails, the contact does not continue, unless http_error is set. Requests are not signed and not retried: put a secret token in a static parameter and check it on your side.

Example — configure an httpCallback node:

curl -u "username:api_key" -H "Accept: application/json" \
  --data-urlencode "x-verb=PUT" \
  --data-urlencode "url=https://example.com/duo/hook" \
  --data-urlencode "parameterName[]=email" --data-urlencode "parameterSource[]=field" \
  --data-urlencode "parameterStaticValue[]=" --data-urlencode "parameterFieldValue[]=email" \
  --data-urlencode "parameterName[]=token" --data-urlencode "parameterSource[]=static" \
  --data-urlencode "parameterStaticValue[]=s3cr3t" --data-urlencode "parameterFieldValue[]=" \
  --data-urlencode "http_error=" --data-urlencode "http_result=" \
  https://app.duo.pt/transactional/{transactional_id}/automation/{automation_id}/node/{node_id}/httpCallback

smsReceivedEntry, smsReceived

Parameter Type Description
inbound_telephone int Optional. ID of one of your inbound SMS numbers. Empty for any of them.
field_to_set int Optional. ID of a custom field that receives the SMS text
value_regex string Optional. Regular expression with one capture group. The captured text is stored instead of the whole SMS (case-insensitive).
period int smsReceived only. Timeout in seconds; on timeout the contact also goes to child. 0 waits without a timeout.

Payload: {"inbound_telephone": null, "set_field": {"field": 45, "source": "regex", "regex": "^YES (\\d+)"}, "timeout": 3600}


DELETE /transactional/<transactional>/automation/<automation>/node/<node>

Deletes the node. The nodes that pointed to it now point to its child. Deleting an ifElse or externalEvent node also deletes the first node of its second branch: delete the nodes of that branch first, from the last to the first.

Returns: transactional, transactionalAutomation and the deleted transactionalAutomationNode.

Example:

curl -u "username:api_key" -H "Accept: application/json" -X DELETE \
  https://app.duo.pt/transactional/{transactional_id}/automation/{automation_id}/node/{node_id}