Skip to main content

Journeys API Integrations

HTTP calls within Journeys

Journeys can integrate with APIs exposed over HTTP.

The functions get, post, put and patch do HTTP calls. HTTP calls allow for the following parameters:

card GetCard, then: PostCard do
get_response = get("https://example.org/endpoint",
timeout: 5_000,
cache_ttl: 60_000,
query: [
["name", "@contact.name"],
["whatsapp_id", "@contact.whatsapp_id"]
])

log("@get_response")
end

card PostCard, then: PutCard do
post_response = post("https://example.org/endpoint",
timeout: 5_000,
cache_ttl: 60_000,
body: """
{"username": @contact.whatsapp_id}
""",
headers: [["content-type", "application/json"]]
)

log("@post_response")
end

card PutCard, then: PatchCard do
put_response = put("https://example.org/endpoint",
timeout: 5_000,
cache_ttl: 60_000,
body: """
{"username": @contact.whatsapp_id}
""",
headers: [["content-type", "application/json"]]
)

log("@put_response")
end

card PatchCard do
patch_response = patch("https://example.org/endpoint",
timeout: 5_000,
cache_ttl: 60_000,
body: """
{"username": @contact.whatsapp_id}
""",
headers: [["content-type", "application/json"]]
)

log("@patch_response")
end
optionuse
bodyThe body of the HTTP request
cache_ttlHow long to cache a response for, in milliseconds (60_000 or 60000 is 60 seconds)
headersA list of lists with HTTP headers, [["X-Foo", "Bar"]] would add the X-Foo header to the HTTP request.
mode"sync" or "async"
queryA list of lists with keys & values to use as query parameters [[key, value]] adds ?key=value to the request
timeoutHow long a request is allowed to take, in milliseconds (5_000 or 5000 is 5 seconds). A value higher than 20 seconds will be set to 20 seconds.
urlThe HTTP endpoint

The HTTP headers and query parameters can be expressions that are evaluated.

The HTTP headers you specify will be included in addition to the following standard HTTP headers:

  • User-Agent: Turn/X.X.X (Journeys Webhook)
  • X-Turn-Journey-UUID: <the-uuid-of-the-journey>
  • X-Turn-Journey-Session-UUID: <the-uuid-of-the-session>
info

Please note that Turn places a strict ceiling on how long it will wait for external services to respond to requests. If your outbound call takes longer than 20 seconds to respond, the webhook call will respond with a timeout error, even if your timeout value is greater than 20 seconds. We advise that you ensure any external services are performant (a less than 10 second respones time is preferred), in order to work within these limits and ensure that the end-user on WhatsApp is not left waiting for lengthy periods of time.

HTTP Connection Errors

If the remote side of your HTTP call times out or drops the connection, the status of the response will be set to null and the body attribute of the response will have the error that occurred.

info

It is the journey author's responsibility to make sure connection errors and timeouts are handled gracefully in order to ensure a good user experience.

Here's an example of handling a connection error:

card Webhook, then: Report do
resp =
post("https://example.org",
body: "hello world!",
timeout: 5_000,
query: [["foo", "bar"]],
headers: [
["content-type", "text/plain; charset=utf-8"]
]
)
end

card Report when not resp.status do
text("""
Apologies, it looks like we are having some connection errors.
The response error was ```@resp.body```

Please try again later!
""")
end

card Report do
text("success")
end

RSS feed integration

Here is an example that reads the RSS feed for the BBC's Global News Podcast and allows users to navigate through the episodes and listen to them.

card Init, "⏮ Latest episode", then: Start do
# Initialize the cursor with a value of zero, this means we always start
# with the first, most recent, entry from the RSS feed
cursor = 0
# Use the public API at rss2json.com to convert the RSS feed that the
# BBC publishes for their world service podcast to a JSON file that
# we can read.
webhook =
get(
"https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fpodcasts.files.bbci.co.uk%2Fp02nq0gn.rss"
)

items = webhook.body.items
# Keep track of the total number of items
total_items = count(items)
end

card Start do
# Read the current item
item = items[cursor]

# send the audio file first, since this is a _big_ file it takes longer to arrive
# on the phone than the messages we send later.
audio("@item.enclosure.link")

# generate the buttons
buttons([Init, NextCard]) do
# send the title as a caption
text("""
*@item.title*

@item.content

_Episode @(cursor + 1) of @(total_items)_
_Published at @(item.pubDate)_
""")
# The feed unfortunately doesn't have images and so we're just using a static
# image from the BBC website
image("https://ichef.bbci.co.uk/images/ic/480x270/p09kz0v5.jpg")
end
end

# We change the text on the button depending on whether or not we have any further
# episodes. If we do we display "Next episode ➡️", if we don't we display "⏮ First episode"
#
# Also if we reach the end of the total number of episodes, we reset the counter to 0
# which has us starting at the beginning again.
card NextCard, "@IF(cursor + 1 < total_items, \"Next episode ➡️\", \"⏮ First episode\")",
then: Start do
cursor = if cursor + 1 < total_items, do: cursor + 1, else: 0
end