How to use a JSON endpoint for dynamic email content

The dynamic content feature allows you to pull content into an email message from a JSON endpoint you control.

The dynamic content option is available at the Setup step, under Advanced options when creating an email message.

The dynamic content option available in the email Setup under Advanced options

Your endpoint can then return JSON like so:


{ "myvariable": "Hello, world", "level1": { "level2": { "myvalue": 24 } } }

Once the data is fetched it can be used in your email messages like so:

{{ json.myvariable }} {{ json.level1.level2.myvalue }} {% if json.level1.level2.myvalue > 23 %} That's a big number {% endif %}

This example is for the Once per campaign mode, read on for further examples of both per campaign and per contact.

Standard options




The HTTPS address to your endpoint. Only https is supported for data security reasons.

HTTP method

You can select which HTTP method will be used when accessing your endpoint.

Once per campaign mode

In this mode the system will fetch content once from your JSON endpoint at the time the email is sent. You might use this if you have a message in a journey you’d like send to all new users, which is updated with the latest info at the time it is sent.

Your endpoint will be sent a POST or PUT request, whichever you choose for the HTTP method. In this case you can ignore the body, since it is only called once for the whole campaign and does not pertain to specific contacts.

NOTE: If the email message is in a journey and you make a change to the JSON content, it may take up to 15 minutes for changes to the content of your JSON endpoint to be reflected in the recipient emails. This is because Ortto uses a cache (with a 15-minute time to live) in order to reduce the number of calls to your endpoint caused by contacts entering the journey at various times.

If you want to immediately stop any more recipients from receiving the old content, we recommend you turn the email shape OFF (which will stop sending), wait 15 minutes to ensure the cache has expired, then turn it back ON. Recipients that were waiting will now receive the email containing the latest JSON content.

If you need to identify which campaign this applies to, the best way to do this is by changing the JSON URL to suit the campaign in question.

You then need to respond to the request with JSON. This should have the MIME type application/json and contain valid JSON in the body.

Example JSON response from your endpoint


{ "word_of_the_day": "Taxonomy", "word_of_the_day_definition": "The branch of science concerned with classification, especially of organisms; systematics.", "meta": { "number_of_logins": 24628 } }

Once the data is fetched it can be used in your email message like so:

Dear {{ person.first-name }}, Today's word of the day is {{ json.word_of_the_day }}! The definition is: {{ json.word_of_the_day_definition }} So far we've had {{ json.meta.number_of_logins }} to our system today, {% if json.level1.level2.myvalue > 100 %} That's a a lot {% elseif %} That's not enough, login please! {% endif %}

Per contact mode

In this mode, you are able to fetch dynamic content for every person who receives the message. This could allow you to do things like show users their current points balance, their last shopping cart, their profile image, and other information which is specific to them.

The data is fetched in batches of 200 (maximum) by sending a POST or PUT to your endpoint with a payload like this:


[ { "id": "0063365f14281d1f8277f102", "email": "" }, { "id": "0063365ceac484b9cb280c01", "email": "" } ]

Each id is Ortto’s ID for that person and the email is there so you can look them up in your database. You can access a contact’s ID using the appropriate API call.

You then need to respond with JSON in this format:


{ "0063365f14281d1f8277f102": {"points": 200, "last_login": "Jan 24th"}, "0063365ceac484b9cb280c01": {"points": 450, "last_login": "Mar 20th"} }

Note that the response payload needs to be a JSON hash, with an entry for each person you wish to send dynamic content for, usually this will be all of them for the batch. The key should be Ortto’s ID for that person, which you can reference from the original payload. The JSON for each person can contain whatever you like and is accessed in the same way as specified above, for example:

Dear {{ person.first-name }}, Your points balance is {{ json.points }}, {% if json.points > 1000 %} You are eligible for the 1000 points badge, login now to receive it. {% elseif %} Only {{ 1000-json.points }} to reach 1000! You last logged in: {{ json.last_login }}. Login today for 10 extra points! {% endif %} From, The Points Company

Error modes for dynamic content

  • You must respond within 15 seconds or the request will timeout.
  • The request will be retried up to 12 times before failing.
  • If the request fails after 12 attempts, the sending of the current batch of emails (up to 200) will fail, even in per campaign mode, and those people will have Invalid email activities.
  • If your JSON is invalid, the campaign sending will fail and the campaign will need to be duplicated and re-sent (once you’ve fixed your JSON response).

Timing notes for dynamic content

  • If a campaign is scheduled, the JSON content will be fetched at the time of each batch sending, rather than when it is scheduled.
  • The system will fetch data per batch, even in per campaign mode.
  • For email campaigns which are sent across multiple user timezones, the JSON will be fetched as each batch is fetched for each timezone, i.e. at the time of actually sending.
  • The JSON will also be fetched when you preview the campaign, and when you send a test email, so if you notice an extra request or two in the logs, that’s it. This also lets you test it before you send.

Other dynamic content notes

  • If you reference a JSON value in your message which does not exist, the output will be blank e.g. {{ json.rubbish }}