# stensul-update-email-content

> Edits content inside an existing draft: copy, buttons, images, alt text, visibility, custom-element variables and URL tracking. Anything you leave out stays as it is.

Source: https://stensul.com/developers/reference/stensul-update-email-content/ · From the live server's tool list, 2026-09-28

- Scope: `emails:write`
- Access: write

Address elements by module id plus element id from the read. Pass `save_version` from your last read so a stale edit fails with a conflict. Custom elements are written by variable value only; markup is never writable. Sent emails cannot be edited: copy them and edit the copy.

## Inputs

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `emailId` | string (24-char hex id) | yes | Email ID. |
| `modules` | array | no | `[{ id, style?, elements: [{ id, type, text?, content?, imageContent?, link?, style?, visibility?, variables?, url_tracking_parameters?, url_tracking_disabled? }] }]`. `type` is required on every element. |
| `save_version` | integer | no | The `save_version` from your last read. Always pass it when the read included one. |
| `url_tracking` | object | no | Campaign-level tracking values. A tracking-only fix can carry this with no `modules`. |
| `module` | object | no | Present in the manifest; use `modules`. |

## Example request

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "stensul-update-email-content",
    "arguments": {
      "emailId": "64f0c0ffee0000000000abcd",
      "save_version": 7,
      "modules": [
        {
          "id": 2817001,
          "elements": [
            {
              "id": 3,
              "type": "text",
              "text": "Roasted this week. On your doorstep by Friday."
            },
            {
              "id": 5,
              "type": "button",
              "text": "Order now",
              "link": {
                "url": "https://northwind.example/order"
              }
            }
          ]
        }
      ]
    }
  }
}
```

## Response

What was applied, tenant warnings, declined changes with reasons, the new `save_version` and a preview.

## Errors

| Status | Message | Cause | Fix |
| --- | --- | --- | --- |
| 401 | Authentication expired or missing | The session's authorization expired. | Sign in again when the host prompts. On a 401 or 403, the server's instructions tell the model to re-check sign-in first. |
| 403 | Insufficient OAuth scope (403): The token lacks the required scope | The API connection doesn't have the scope this tool needs. | An admin adds the scope to the API connection. Then sign in again: adding a scope revokes existing tokens. |
| 403 | API error (403): Action is unauthorized. | The signed-in user's role lacks the permission, for example `edit_campaign`. | Grant the permission to the user's role in Admin > Users & Roles. It works for other users on the same connection. |
| declined | Content declined by a guardrail | A value is outside what the template allows: an off-palette color, a size out of range, locked text. | Keep the value within the guardrail or make no change. Guardrails are enforced by Stensul and cannot be overridden from a chat. |
| 409 | Version conflict | The email changed since your last read, and you passed its old `save_version`. | Read the email again and re-apply your change on the current version. Never retry by dropping `save_version`. |
| 403 | API error (403): Sent emails cannot be edited. | The email has already been sent. | Nothing to fix. Copy it and edit the copy. |
