Skip to content

Message API - Fields Reference

This article describes all fields available through the Message API. Use this as a reference when reading, creating, updating, or sending messages.


The Message API lets you read, create, update, delete, send, and cancel the email messages in your Envoke account. A message is one email campaign: its settings (name, sender, subject, recipients, send time) and its HTML content. Messages are created as drafts; only a Draft message can be changed, and sending moves it through the statuses described under status.

The Message API answers on /v2 only. Related resources have their own APIs: templates whose HTML a new message can copy through template_id are managed with the Template API, message groups (folders) with the Group API, the tags and subscriptions a message is sent to with the Tag API and Subscription API, and sender identities with the Sender API. Sending and cancelling are covered in Sending.

The API returns no delivery, open, or click statistics; the only counts it gives are the recipient estimate in a send response.

All endpoints require HTTP Basic Auth using your API ID and API Key.


Field Type Read-only Required on Create Description
id string Yes — Envoke message ID, assigned on creation.
external_id string Yes — Externally visible identifier used in the preview URL.
preview_url string Yes — Public URL to view the message in a browser.
create_time string Yes — When the message was created, in the account time zone.
modified_time string Yes — When the message was last changed, in the account time zone.
name string No No Internal name. Generated if omitted.
group string No No Name of the message group (folder), or "".
status string Yes — Current status, e.g. Draft or Sent. Changed only by sending and cancelling.
send_time string No No Scheduled send time in the account time zone, or "".
sender string No No Sender identity as Name <email>. Defaults to the account’s default sender.
reply_to string No No Reply-to identity as Name <email>. No default.
subject string No No Subject line. Required to send.
language string No No Language of the message, en or fr. Defaults to en.
recipient_subscription string No No Subscription the message is sent to, or "".
recipient_tag string No No Tag the message is sent to, or "".
recipient_segment string No No Segment the message is sent to, or "".
recipient_language string No No Send only to contacts with this language, en or fr, or "" for all.
publish_archive boolean No No Whether the message is published to the account’s web archive.
utm_tag_links boolean No No Whether UTM tracking parameters are added to links.
html string Write-only One of html or template_id The message HTML. Returned only on a single GET with include_html.
template_id integer Write-only One of html or template_id ID of a template or message to copy HTML from. Create only. Never returned.
replace object Write-only No Placeholder text to replace in the HTML when it is saved. Never returned.

Every readable field is present in a read response unless fields is used to select some. An unset text value is returned as "", except sender and reply_to, which are returned as null when no identity is set (see sender and reply_to). Read-only fields sent on create or update are silently ignored. A message object returned by a GET cannot always be sent back as is: a null sender or reply_to fails as Invalid 'reply_to': '', a send_time of "" fails as not in the future, and a subject of "" fails as shorter than 6 characters, so remove those values first.

  • Type: String
  • Read-only: Yes — assigned automatically on creation.
  • Returned value (read): The message ID as a string.
  • Example: "2001"

Identifies the message in the URL of single GET, update, delete, send, and cancel requests. The one exception to the string type is the response of POST /v2/messages/send (create and send), whose result_data.id is an integer (see Sending).


  • Type: String (32 characters)
  • Read-only: Yes — assigned automatically on creation.
  • Example: "3e8b1f6c9a2d4e7b0c5f8a1d3e6b9c2f"

An externally visible identifier used to build preview_url. Not the same as id.


  • Type: String
  • Read-only: Yes — built from your account’s external ID and the message’s external_id.
  • Format: https://{domain}/m/{account_external_id}/m/{external_id}
  • Example: "https://engage.envoke.com/m/a7c3e9f1b5d2/m/3e8b1f6c9a2d4e7b0c5f8a1d3e6b9c2f"

A public URL that shows the message in a browser without logging in. {domain} is your account’s custom domain if it has one, and otherwise Envoke’s message domain; it is not the API host. Use the URL as returned rather than building it yourself.


  • Type: String
  • Read-only: Yes — set automatically on creation.
  • Returned value (read): Y-m-d H:i:s in the account time zone, with no offset.
  • Example: "2026-10-06 10:15:42"

See Dates and time zones.


  • Type: String
  • Read-only: Yes — updated automatically.
  • Returned value (read): Y-m-d H:i:s in the account time zone, with no offset.
  • Example: "2026-10-06 11:02:08"

Changes on every successful update, even one that changes no value, and when a message is sent or cancelled. The list is sorted by this field, newest first, by default.


  • Type: String, at least 3 characters
  • Read-only: No
  • Required on create: No — if omitted, the message is named API Message {Y-m-d H:i:s} from the time of the request.
  • Example: "Spring Season Announcement"

The internal name shown in the Envoke message list; recipients do not see it. Too short: The field 'name' must be at least 3 characters long. The API sets no maximum; a longer name is cut to its first 300 characters without an error.

The generated name uses Envoke’s server clock, which can differ from the account time zone used by create_time.


  • Type: String
  • Read-only: No
  • Required on create: No
  • Accepted values (write): The name or ID of an existing message group. Names are matched ignoring case, and a space and an underscore are treated as the same. "" or "0" removes the message from its group.
  • Returned value (read): The group name, or "" when the message has no group.
  • Example: "Productions"

Only message groups can be used; template groups and contact groups are not accepted. A group is not created automatically: an unknown value fails with Invalid 'group': 'Playbills' and the list of valid groups in allowed_values (see Error Responses). Create groups with the Group API. A message created through this API has no group unless group is set.


  • Type: String
  • Read-only: No
  • Required on create: No
  • Accepted values (write): A future date and time, read as account-local time, e.g. "2026-10-20 09:00:00".
  • Returned value (read): Y-m-d H:i:s in the account time zone, or "" when no time is set.
  • Example: "2026-10-20 09:00:00"

Setting send_time on a draft does not schedule it; the message is scheduled only when it is sent. See Dates and time zones for how the value is read and why it cannot be cleared.


  • Type: String, at least 6 characters
  • Read-only: No
  • Required on create: No — but a message cannot be sent without one.
  • Example: "Our spring season is here"

Defaults to "". Too short: The field 'subject' must be at least 6 characters long. The API sets no maximum; a longer subject is cut to its first 360 characters without an error.


  • Type: String
  • Read-only: No
  • Required on create: No — defaults to en.
  • Accepted values (write): en or fr (case-insensitive), or the IDs 1 (English) or 3 (French). "" or "0" sets en.
  • Returned value (read): "en" or "fr".
  • Example: "fr"

The language the message is written in. Any other value fails with Invalid 'language': 'de' and allowed_values {"1":"en","3":"fr"}. To choose which contacts receive the message by language, use recipient_language.


  • Type: Boolean
  • Read-only: No
  • Required on create: No — defaults to false.
  • Accepted values (write): true, "true", 1, "1", "yes", or "on" publishes. Anything else is treated as false.
  • Returned value (read): true or false.
  • Example: true

When true, the message is published to your account’s public message archive.


  • Type: Boolean
  • Read-only: No
  • Required on create: No — defaults to your account’s link analytics setting.
  • Accepted values (write): As for publish_archive.
  • Returned value (read): true or false.
  • Example: true

When true, Envoke adds UTM tracking parameters to the links in the message. On create, the default is true if link analytics is turned on in your account preferences and false otherwise.


  • Type: String
  • Read-only: Yes — changed only by sending and cancelling.
  • Returned value (read): One of the status names below.
  • Example: "Draft"
Code Name How a message gets there What the API allows
0 Draft Created through the API or in Envoke; a cancelled send; a scheduled message that is deleted. Update, send, delete.
1 Scheduled Sent through the API or launched in Envoke, before its send time. Cancel (back to Draft), delete (also unschedules it).
2 AB test in progress An A/B test launched in Envoke. A/B tests cannot be set up through the API. Cancel, delete.
4 Queuing The send has started: Envoke is selecting the recipients. Cancel is accepted but does not stop delivery (see Cancel); delete.
5 Sending Recipients are queued and emails are going out. Cancel is accepted but does not stop delivery. Cannot be deleted.
7 Transactional A transactional message set up in Envoke. Delete.
8 Nurture A message in a nurture campaign (autoresponder). Delete, unless it is part of a nurture series.
9 Sent Every email has been handed off for delivery. Delete.
  • A message in any status other than Draft cannot be updated or sent: Only 'Draft' messages can be updated, this message has the status: 'Sent'.
  • status is read-only. Sending it in a request body has no effect.
  • Status names are returned exactly as shown. In filters, a name or a code can be used (see Read).
  • The list includes messages in every status, including Nurture and Transactional messages created in Envoke.

These three write-only fields set the message’s content.

  • Type: String, at most 200 kB (204,800 bytes)
  • Read-only: Write-only — returned only by GET /v2/messages/{id}?include_html=1.
  • Required on create: One of html or template_id.
  • Example: "<html><body><h1>Spring Season</h1><p>Curtain up on {SHOW_TITLE}.</p></body></html>"

The complete HTML of the message, stored as sent. Envoke does not check or clean it. If it contains the <meta name="TEMPLATE_VERSION" tag that Envoke’s drag-and-drop editor writes, the message opens in that editor in Envoke; otherwise it opens in the HTML editor.

Rule Behaviour
Size Over 200 kB fails with The field 'html' may not be longer than 200kB. The size is checked on the HTML you send, before replace is applied.
Empty value "" counts as not sent. On update, the content cannot be blanked.
Returned Never in list, create, update, send, or cancel responses. Only on a single GET with include_html (see Read).
With template_id When both are sent on create, html is used and template_id is ignored, with no error.
On update Replaces the whole content of the message.
  • Type: Integer (a numeric string such as "1901" is also accepted)
  • Read-only: Write-only — never returned.
  • Required on create: One of html or template_id.
  • Example: 1901

The ID of a template, or of another message in your account, whose HTML the new message copies. Templates shared with your account can be used as well as your own. An ID that matches nothing fails with Nothing found for 'template_id': 1999.

Only the HTML (and which editor the content opens in) is copied. The source’s subject, sender, reply-to, language, group, and recipients are not; set them in the same request. The copy is independent: later changes to the template do not change the message.

template_id is accepted on create only. On update it fails with The field 'template_id' is not valid for updating a message; send new html instead.

  • Type: Object of strings
  • Read-only: Write-only — never returned.
  • Required on create: No
  • Format: { "{PLACEHOLDER}": "replacement text" }
  • Example: { "{SHOW_TITLE}": "The Pirates of Penzance", "{CURTAIN_TIME}": "7:30 p.m." }

Replaces placeholder text in the message HTML once, when the message is saved. The HTML is stored with the replacements already made; nothing is replaced later at send time, and the replacement is the same for every recipient. For per-recipient content, use Envoke merge tags in the HTML instead.

Rule Behaviour
On create Applied to html, or to the HTML copied through template_id.
On update Applied only to the html sent in the same request. Without html: The field 'html' is required for content 'replace' data. You cannot run a replacement over the HTML a message already has.
Keys Every key must start with { and end with }, e.g. {SHOW_TITLE}.
Values Every value must be a string. It is inserted as is, without escaping, so it can contain HTML.
Unmatched keys A key that does not appear in the HTML is ignored.
Order Keys are applied in the order sent, so a later key can match text inserted by an earlier value.
Protected links A key may not contain {@pref-, {@consent-url, or {@consent-prefpage-url, so the preference and consent links in your footer cannot be replaced.
Empty {} is ignored.

Any other shape (a list, a non-string value, a key without braces, a protected key) fails with Invalid 'replace' data, which does not say which entry is wrong.

The size limit on html is checked before the replacement, so the saved HTML can be larger than 200 kB if the values are long.

Use case 1: Fill in a template designed in Envoke. Build the template once in Envoke with placeholder text, then create each message from it with template_id and fill in the details:

{
"name": "Pirates of Penzance - Opening Night",
"template_id": 1901,
"replace": { "{SHOW_TITLE}": "The Pirates of Penzance", "{CURTAIN_TIME}": "7:30 p.m." }
}

Use case 2: Insert a block of HTML from your own system into a master template. The template holds your design, header, and footer, with one placeholder where the content goes. Your system builds the content and sends it as the value:

{
"name": "Weekly Show Listings",
"template_id": 1902,
"replace": { "{SHOW_LISTINGS}": "<h2>This week</h2><p>Fri 7:30 p.m. - The Pirates of Penzance</p><p>Sat 2:00 p.m. - Matinee</p>" }
}

Use case 3: Fill in your own HTML. When your system already produces the HTML, send it with placeholders and the values in the same request:

{
"html": "<html><body><p>Dear patron, {SHOW_TITLE} opens on {OPENING_DATE}.</p></body></html>",
"replace": { "{SHOW_TITLE}": "The Pirates of Penzance", "{OPENING_DATE}": "Friday, October 23" }
}

Envoke merge tags such as {@first_name} are not protected keys. Avoid using them as replace keys, or the merge tag is replaced for every recipient.


  • Type: String
  • Read-only: No
  • Required on create: No. sender defaults to the account’s default sender; reply_to has no default.
  • Accepted values (write): An identity ID, the identity as Name <email>, or the email address alone. "" or "0" clears the field.
  • Returned value (read): "Name <email>", or null when no identity is set.
  • Example: "Box Office <boxoffice@example.org>"

Both fields take one of the sender identities (From addresses) set up in your account; list them with the Sender API. A value is resolved in this order:

Step Value Result
1 Digits that match an identity ID, e.g. "412" or 412 That identity.
2 Name <email> matching an identity, ignoring case and treating a space and an underscore as the same, e.g. "box office <boxoffice@example.org>" That identity.
3 "" or "0" The field is cleared.
4 A valid email address, e.g. "boxoffice@example.org" The first identity with that address. The address must match exactly, including case. If none matches: No sender identity found for email: 'tickets@example.org' (or No reply_to identity found for email: ...).
5 Anything else Invalid 'sender': 'Box Office' (or Invalid 'reply_to': ...).

Errors from steps 4 and 5 include the account’s identities in allowed_values, keyed by ID, as Name <email>.

When sender is omitted on create (or sent as ""), the message uses the identity marked as the account default. On update, "" clears the sender without falling back to the default, and a message with no sender cannot be sent: The field 'sender' must be set. reply_to is optional; when it is not set, replies go to the sender address.


Three fields choose who a message is sent to, and a fourth narrows the choice by language.

Field Accepted values (write) Returned value (read)
recipient_subscription The name or ID of a subscription, including your account’s mandatory consent subscription. The subscription name, or "".
recipient_tag The name or ID of a tag. The tag name, or "".
recipient_segment The name or ID of a segment. The segment name, or "".
recipient_language en, fr, 1, or 3. "" or "0" clears it. "en", "fr", or "" (all languages).

Names are matched ignoring case, with a space and an underscore treated as the same. An unknown name or ID fails with Invalid 'recipient_tag': 'Cast List' (and similarly for the other fields) with the valid values in allowed_values when the account has 50 or fewer of them. "" or "0" clears a field. Example values: "Season News", "Cast Call", "Lapsed Subscribers".

Tags and subscriptions can be created with the Tag API and Subscription API. Segments cannot be created through any API; create them in Envoke first.

Rule Behaviour
Tag or segment, not both recipient_tag and recipient_segment cannot both be set in one request: Only one of the fields 'recipient_tag' or 'recipient_segment' may be provided.
Subscription alone The message goes to the contacts subscribed to it.
Tag or segment alone The message goes to the contacts with the tag, or in the segment.
Subscription and a tag or segment The subscription narrows the tag or segment: the message goes only to contacts who are in the tag or segment and subscribed.
Mandatory consent When recipient_subscription is your account’s mandatory consent subscription, the message is sent as a mandatory (consent-exempt) message, with the subscription’s description as the stated reason.
recipient_language Only contacts whose language matches are sent the message. When it matches your account’s default language, contacts with no language set are included too.
Sending At least one of recipient_subscription, recipient_tag, or recipient_segment must be set to send: At least one of the fields 'recipient_subscription', 'recipient_tag', or 'recipient_segment' must be set.

On update, the recipient fields are replaced as a set. If a request includes any one of recipient_subscription, recipient_tag, or recipient_segment, the other two are cleared unless they are in the same request. For example, {"recipient_tag": "Cast Call"} on a message sent to the Season News subscription leaves it sent to the tag only. To keep a subscription and change the tag, send both. {"recipient_subscription": ""} clears all three. recipient_language is not affected.

On read, recipient_tag is "" when the message is sent to a subscription alone.

Contacts are selected when the send starts, not when the fields are set, and only those whose consent allows sending are emailed (see Recipient counts).


Field On write On read
create_time Ignored Y-m-d H:i:s in the account time zone
modified_time Ignored Y-m-d H:i:s in the account time zone
send_time A future date and time, read as account-local time Y-m-d H:i:s in the account time zone, or ""

The account time zone is the one set in your Envoke account preferences, for example America/Toronto. Returned times carry no offset or Z; they are wall-clock times in that zone. Daylight saving time applies.

Send send_time as account-local time without an offset, for example "2026-10-20 09:00:00" for 9:00 a.m. in the account’s zone. An offset or Z in the value is dropped, and the remaining wall-clock time is still read in the account time zone: "2026-10-20T09:00:00Z" and "2026-10-20T09:00:00-07:00" both mean 9:00 a.m. account time, not 9:00 UTC or 9:00 Pacific.

send_time value Result
A future time Stored, and shown in later responses.
A time in the past The field 'send_time' must be in the future
A value that is not a date Invalid 'send_time': 'next Friday evening'
"" or null Read as the current time, which fails as not in the future.

send_time cannot be cleared. Once a message has a send time, no value returns it to "". A send time also survives a cancel: a cancelled message goes back to Draft with its old send_time, and sending it again without a new send_time reuses the stored time (see Send a draft).


Validation errors (missing or invalid fields, rule violations, bad filters):

{
"success": false,
"result_text": "Validation failed, see 'result_data' -> 'errors'",
"result_data": {
"errors": [
"The field 'name' must be at least 3 characters long",
"'headline' is not a valid field"
]
}
}

When a lookup field (group, sender, reply_to, language, recipient_language, recipient_subscription, recipient_tag, recipient_segment) has an unknown value, result_data also carries allowed_values, an object of the valid options keyed by ID. It is included only when the account has 50 or fewer options for that field:

{
"success": false,
"result_text": "Validation failed, see 'result_data' -> 'errors'",
"result_data": {
"errors": ["Invalid 'group': 'Playbills'"],
"allowed_values": {
"group": { "31": "Productions", "32": "Newsletters" }
}
}
}

Plain errors. Some errors use result_data: [] with the message in result_text instead of the validation envelope: Invalid JSON, Invalid request, the ID mismatch on update, send, and cancel, Message content error, the delete errors, and the zero-recipient send result (which carries data; see Sending).

{
"success": false,
"result_data": [],
"result_text": "Item with id '2099' not found"
}

Empty or unreadable body. POST /v2/messages, PATCH /v2/messages/{id}, and POST /v2/messages/send with no body, an empty body, or {} return the validation envelope with Request body may not be empty. The send-by-ID and cancel endpoints accept an empty body. A body that is not valid JSON returns the plain envelope with Invalid JSON.

All error responses above use HTTP status 400. An invalid API ID or key returns 401 with the body {"success":false,"result_data":[],"result_text":"Invalid credentials"}. A valid key on a closed or inactive account returns 403 with the JSON body {"success":false,"result_data":[],"result_text":"This account has been closed."} (or "This account is not active.") Exceeding your account’s API limit returns 429 with success: false and a result_text naming the limit.

Not-found behaviour varies by method:

  • GET /messages/{id} — when the ID is not found, returns an empty array ([]). Deleted messages and templates are not found.
  • PATCH /messages/{id} — returns a validation error:
{
"success": false,
"result_text": "Validation failed, see 'result_data' -> 'errors'",
"result_data": {
"errors": ["No message found for 'id': 2099"]
}
}
  • POST /messages/{id}/send — the same validation error as PATCH.
  • POST /messages/{id}/cancel — returns the validation error for a status that cannot be cancelled, with the status left blank: Sending cannot be cancelled for a message with 'status': .
  • DELETE /messages/{id} — returns the plain envelope: {"success":false,"result_data":[],"result_text":"Item with id '2099' not found"}.

Full reference, OpenAPI description, and Postman collection: developers.envoke.com.