# Message API - Update

Use this endpoint to change a draft message: its settings, its recipients, or its HTML.

---

## Endpoint

```
PATCH https://e1.envoke.com/v2/messages/{ID}
```

Replace `{ID}` with the Envoke message ID. Authentication is via HTTP Basic Auth using your API ID and API Key. The request body must be a single JSON object. There is no endpoint for updating several messages in one request.

---

## Request Body

Send only the fields you want to change. Fields you leave out keep their current values, with one exception: the recipient fields are replaced as a set (see [Recipients](#recipients)).

| Field | Type | Description |
|---|---|---|
| `name` | string | Internal name. At least 3 characters. |
| `group` | string | Name or ID of an existing message group. `""` removes the group. |
| `subject` | string | Subject line. At least 6 characters. |
| `sender` | string | Identity ID, `Name <email>`, or email address. `""` clears it. |
| `reply_to` | string | Identity ID, `Name <email>`, or email address. `""` clears it. |
| `language` | string | `en` or `fr`. `""` sets `en`. |
| `recipient_subscription` | string | Name or ID of a subscription. `""` clears it. |
| `recipient_tag` | string | Name or ID of a tag. Not with `recipient_segment`. `""` clears it. |
| `recipient_segment` | string | Name or ID of a segment. Not with `recipient_tag`. `""` clears it. |
| `recipient_language` | string | `en` or `fr`. `""` clears it (all languages). |
| `publish_archive` | boolean | Publish the message to the web archive. |
| `utm_tag_links` | boolean | Add UTM parameters to links. |
| `send_time` | string | A future account-local time. Stored only; it does not schedule the message. |
| `html` | string | New HTML for the message, replacing all of its content. Max 200 kB. |
| `replace` | object | Placeholder text to replace in the `html` sent in the same request. |
| `id` | string or integer | Optional. If sent, it must match `{ID}` (see [ID Mismatch](#id-mismatch)). |

`external_id`, `preview_url`, `create_time`, `modified_time`, and `status` are read-only and silently ignored. A message object returned by a GET can be edited and sent back only after removing a `null` `sender` or `reply_to`, an empty `send_time`, and an empty `subject`, which would otherwise fail validation (see the [Fields reference](https://envoke-developers.pages.dev/v2/messages/fields/#field-details)). `template_id` cannot be used on update. Any other key fails with `'headline' is not a valid field`. See the [Fields reference](https://envoke-developers.pages.dev/v2/messages/fields/) for accepted values and the full rules for each field.

Every successful update sets `modified_time`, even when no value changed.

---

## Only drafts can be updated

A message can be updated only while its status is `Draft`. Any other status fails with:

```json
{
  "success": false,
  "result_text": "Validation failed, see 'result_data' -> 'errors'",
  "result_data": {
    "errors": ["Only 'Draft' messages can be updated, this message has the status: 'Scheduled'"]
  }
}
```

To change a scheduled message, [cancel](https://envoke-developers.pages.dev/v2/messages/sending/#cancel) it first, which returns it to `Draft`, then update it and send it again. Sent, nurture, and transactional messages cannot be changed through the API.

---

## Recipients

`recipient_subscription`, `recipient_tag`, and `recipient_segment` are treated as one setting. **When a request includes any one of them, the other two are cleared unless they are also in the request.**

| Current recipients | Request | Result |
|---|---|---|
| Subscription `Season News` | `{"recipient_tag": "Cast Call"}` | Tag `Cast Call` only; the subscription is removed. |
| Subscription `Season News` | `{"recipient_subscription": "Season News", "recipient_tag": "Cast Call"}` | Contacts with `Cast Call` who are subscribed to `Season News`. |
| Subscription and tag | `{"recipient_subscription": ""}` | No recipients. |
| Tag `Cast Call` | `{"recipient_segment": "Lapsed Subscribers"}` | Segment `Lapsed Subscribers` only. |
| Any | `{"subject": "New subject line"}` | Unchanged. |

`recipient_tag` and `recipient_segment` cannot both be set: `Only one of the fields 'recipient_tag' or 'recipient_segment' may be provided`. `recipient_language` is a separate setting and is never cleared by this rule. See [Recipients](https://envoke-developers.pages.dev/v2/messages/fields/#recipients) for how the fields combine when the message is sent.

---

## Replacing the HTML

Sending `html` replaces the whole content of the message. There is no way to change part of the HTML in place; read the current HTML with `GET /v2/messages/{ID}?include_html=1`, change it, and send it back.

`replace` works on update only together with `html` in the same request. The replacements are made in the HTML you send, and the result is saved. Without `html`, the request fails with `The field 'html' is required for content 'replace' data`; a replacement cannot be run over the HTML the message already has.

```json
{
  "html": "<html><body><h1>{SHOW_TITLE}</h1><p>Curtain at {CURTAIN_TIME}.</p></body></html>",
  "replace": { "{SHOW_TITLE}": "The Pirates of Penzance", "{CURTAIN_TIME}": "8:00 p.m." }
}
```

`template_id` is not accepted: `The field 'template_id' is not valid for updating a message`. To switch a draft to a template's HTML, read the template with the [Template API](https://envoke-developers.pages.dev/v2/templates/read/) and send its HTML as `html`.

---

## Clearing Values

| Field | To clear | Notes |
|---|---|---|
| `group` | `""` or `"0"` | The message has no group. |
| `sender` | `""` or `"0"` | No fallback to the default sender. The message cannot be sent until a sender is set again. |
| `reply_to` | `""` or `"0"` | |
| `recipient_subscription`, `recipient_tag`, `recipient_segment` | `""` or `"0"` | Clearing one clears all three (see [Recipients](#recipients)). |
| `recipient_language` | `""` or `"0"` | Sends to all languages. |
| `language` | — | `""` or `"0"` sets `en`. |
| `name`, `subject` | — | Cannot be cleared; `""` fails the minimum-length check. |
| `html` | — | Cannot be cleared; `""` is ignored. |
| `send_time` | — | Cannot be cleared once set (see [Dates and time zones](https://envoke-developers.pages.dev/v2/messages/fields/#dates-and-time-zones)). |

Use the string `"0"`, not the number `0`: a JSON number `0` fails as an invalid value, e.g. `Invalid 'group': '0'`.

---

## ID Mismatch

If the body includes `id` and it differs from `{ID}`, the request fails with a plain envelope, not the validation envelope:

```json
{
  "success": false,
  "result_data": [],
  "result_text": "Provided 'id': 2001 does not match 'id' in request body (2002)"
}
```

`"2001"` and `2001` count as the same. The same rule and message apply to `POST /v2/messages/{ID}/send` and `POST /v2/messages/{ID}/cancel`.

---

## Response

On success, the API returns the updated message in full, in the same shape as a [read](https://envoke-developers.pages.dev/v2/messages/read/#response):

```json
{
  "success": true,
  "result_text": "Updated",
  "result_data": {
    "id": "2002",
    "name": "Auditions Open",
    "status": "Draft",
    "subject": "Auditions start Monday at 7",
    "recipient_subscription": "",
    "recipient_tag": "Cast Call",
    "recipient_segment": "",
    "modified_time": "2026-10-06 11:30:17"
  }
}
```

*(`result_data` shortened; the response contains every field.)*

`result_data.id` is a string. The HTML is not included.

---

## Validation Errors

```json
{
  "success": false,
  "result_text": "Validation failed, see 'result_data' -> 'errors'",
  "result_data": {
    "errors": [
      "No message found for 'id': 2099"
    ]
  }
}
```

The checks run in the order below. Each of the first six returns on its own; the field checks are then collected and returned together, before the `replace` checks.

| Error | Cause |
|---|---|
| `Request body may not be empty` | No body, an empty body, or `{}`. |
| `No message found for 'id': 2099` | No message with that ID in your account. Deleted messages and templates are not found. |
| `Only 'Draft' messages can be updated, this message has the status: 'Sent'` | The message is not a draft. |
| `The field 'template_id' is not valid for updating a message` | `template_id` was sent. |
| `The field 'html' may not be longer than 200kB` | `html` is over 204,800 bytes. |
| `Only one of the fields 'recipient_tag' or 'recipient_segment' may be provided` | Both were sent with values. |
| Field errors | `'headline' is not a valid field`, the `name` and `subject` length errors, unknown lookup values with `allowed_values`, and the `send_time` errors, as listed for [create](https://envoke-developers.pages.dev/v2/messages/insert/#validation-errors). |
| `The field 'html' is required for content 'replace' data` | `replace` was sent without `html`. |
| `Invalid 'replace' data` | `replace` is not an object of strings with `{...}` keys, or a key targets a preference or consent link. |

`Invalid JSON` and the [ID mismatch](#id-mismatch) use the plain envelope with `result_data: []`.

---

## Examples

### Change the subject and HTML

```bash
curl -X PATCH -gs \
  -u "$API_ID:$API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject": "Auditions start Monday at 7", "html": "<html><body><h1>Auditions Open</h1><p>Monday at 7 p.m. in the rehearsal hall.</p></body></html>"}' \
  "https://e1.envoke.com/v2/messages/2002"
```

### Send to a tag instead of a subscription

```bash
curl -X PATCH -gs \
  -u "$API_ID:$API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"recipient_tag": "Cast Call"}' \
  "https://e1.envoke.com/v2/messages/2002"
```

If the message was going to the `Season News` subscription, it now goes to the `Cast Call` tag only.

### Clear the reply-to address

```bash
curl -X PATCH -gs \
  -u "$API_ID:$API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reply_to": ""}' \
  "https://e1.envoke.com/v2/messages/2001"
```

`reply_to` is then returned as `null`.

### Set a send time

```bash
curl -X PATCH -gs \
  -u "$API_ID:$API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"send_time": "2026-10-20 09:00:00"}' \
  "https://e1.envoke.com/v2/messages/2002"
```

The message stays a `Draft`. The time is used when it is [sent](https://envoke-developers.pages.dev/v2/messages/sending/#send-a-draft).

### New HTML with placeholders

```bash
curl -X PATCH -gs \
  -u "$API_ID:$API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"html": "<html><body><h1>{SHOW_TITLE}</h1><p>Curtain at {CURTAIN_TIME}.</p></body></html>", "replace": {"{SHOW_TITLE}": "The Pirates of Penzance", "{CURTAIN_TIME}": "8:00 p.m."}}' \
  "https://e1.envoke.com/v2/messages/2003"
```

### Update a message that is not a draft

```bash
curl -X PATCH -gs \
  -u "$API_ID:$API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject": "Last chance for presale seats"}' \
  "https://e1.envoke.com/v2/messages/2001"
```

Message `2001` is `Scheduled`, so the request fails with `Only 'Draft' messages can be updated, this message has the status: 'Scheduled'` (see [Only drafts can be updated](#only-drafts-can-be-updated)).

### Use `template_id` on update

```bash
curl -X PATCH -gs \
  -u "$API_ID:$API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"template_id": 1901}' \
  "https://e1.envoke.com/v2/messages/2002"
```

```json
{
  "success": false,
  "result_text": "Validation failed, see 'result_data' -> 'errors'",
  "result_data": {
    "errors": ["The field 'template_id' is not valid for updating a message"]
  }
}
```

### Message not found

```bash
curl -X PATCH -gs \
  -u "$API_ID:$API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject": "Auditions start Monday at 7"}' \
  "https://e1.envoke.com/v2/messages/2099"
```

Returns the validation error shown under [Validation Errors](#validation-errors).

---

*Note: The curl examples above are formatted for readability. All requests must use properly encoded URLs.*

*Full reference, OpenAPI description, and Postman collection: [developers.envoke.com](https://developers.envoke.com).*
