# Draft a Klaviyo Email Campaign From a Product Launch

Yes. An agent writes a short launch email, chooses an existing Klaviyo list or segment, and creates the campaign as an unsent draft.

[Run this on Rush](https://cloud.getrush.ai/run/draft-email-campaign/klaviyo/).

## What the agent does

The launch brief is an attachment, not a repository. The session input in the API is either a string or a list of blocks. This page shows the instructions as a string. To hand over the brief, send input as blocks instead: a text block with those instructions, and a document or file block for the brief. A document can be a public https URL or base64. A private file is uploaded first with POST /o/{org}/files, PUT to the upload_url, then referenced by file id. The agent finds the brief at /workspace/.rush/attachments/<name>. There is still no GitHub resource on the run.

Klaviyo is the connection named on the session, the remote MCP server at https://mcp.klaviyo.com/mcp from Rush's connector catalog. The catalog says the account needs an Owner, Admin or Manager role, which matters here because this run creates a campaign. Read tools alone cannot do the job. The agent may call get_lists, get_list, get_segments and get_segment to choose an audience that already exists. It may call create_email_template or create_dnd_email_template for the body, create_campaign for the draft, and assign_template_to_campaign_message to attach the template. It may call get_campaign afterwards to confirm status. It must not call send_campaign, update_campaign in order to schedule, or refresh_campaign_recipient_estimation if that would be mistaken for a send. Klaviyo's tool table marks send_campaign as not read-only.

The copy constraints are in the prompt: one subject line, preview text, and a body under 200 words, drawn from the brief. The audience choice is one existing list or segment, with a sentence on why that group matches the brief (product buyers, a region, a list the brief names). The agent does not call create_list or create_segment. If nothing existing fits, it still creates the draft only when a list is a reasonable default, and the reasoning says the fit is weak, naming the lists it considered. It does not invent a list id.

What it returns is the draft's identity and a link if get_campaign returned one, plus the audience reasoning. The email body is also written to launch-draft.md in the sandbox so a reviewer can read the words without opening Klaviyo. The campaign stays a draft. The agent does not set a send time.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Read the attached launch brief. Write a subject line, preview text and an email under 200 words. Choose the most relevant existing list or segment from get_lists and get_segments. Create an email template, create the campaign in Klaviyo as a draft with that audience, and assign the template to the campaign message. Do not call send_campaign, do not schedule a send, and do not create a new list or segment. Return the draft id, any link get_campaign returned, and your audience reasoning. Also write the subject, preview and body to launch-draft.md.",
  "connections": [
    "klaviyo"
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

Two things come back. launch-draft.md is an artifact: subject, preview text, the body, the list or segment id, and the reasoning. The Klaviyo draft exists in the account, and the turn's result quotes the draft id from create_campaign and the status from the follow-up get_campaign. The session read shows both the artifact url (an hour, no key, refreshed on the next read) and result. A reviewer who only has the webhook sees result up to 16 KiB, which is enough for the id and the reasoning, and fetches the file if they want the full body.

The transcript is the proof the draft was not sent. A reviewer looks for create_campaign, create_email_template or create_dnd_email_template, and assign_template_to_campaign_message, and checks that send_campaign is absent. They open the draft in Klaviyo and compare the subject with launch-draft.md. They also check the audience id against get_list or get_segment output, so a typo cannot point the draft at the wrong segment.

The draft waits for a person to send it. This run does not schedule. If the copy is wrong, a follow-up message can ask for a revised template on the same campaign id; the sandbox is parked and still has the brief under /workspace/.rush/attachments. Send remains a human action in Klaviyo, or a different request that says to call send_campaign.

## Run it on every product launch brief filed

When a launch brief is approved in your tracker, the worker that already handles that event uploads the brief with POST /o/{org}/files and starts this session, with the brief's id as the Idempotency-Key. A retried webhook from the tracker then returns the same run instead of creating a second draft. The input blocks carry the text instructions and the file id.

On run.completed, your webhook handler reads result for the draft id and posts it back to the tracker. Verify webhook-signature against the raw body with the whsec_ secret. If the turn ends run.failed, leave the tracker item open; do not assume a draft exists because the call returned 202. 202 means the session started, not that Klaviyo accepted a campaign.

## Why run it on Rush?

A blank Klaviyo campaign is a subject line, a template and an audience picker, and the same screen has a send button. This run creates the draft through named tools and stops before send_campaign. The brief arrives as an attachment in the sandbox, the Klaviyo token stays on the connection, and the laptop never holds either. The catalog role requirement (Owner, Admin or Manager) is why a read-only analyst key will fail the create, which is the failure you want.

The draft id in result and the body in launch-draft.md are the same words, and the transcript shows the create calls. A reviewer can reject the copy without hunting through Klaviyo's template HTML. Several launches in one week are several sessions, not one long chat, up to max_concurrent on the key.

## Frequently asked questions

### Will the campaign send?

No. send_campaign is forbidden in the prompt, and the agent is told not to set a send time. Klaviyo's tool table lists send_campaign separately from create_campaign. Check the transcript for that tool name before anyone treats the draft as waiting. A person sends it from Klaviyo after reading launch-draft.md.

### How do I attach the brief if the example input is a string?

The string is the instructions. The API also accepts input as a list of blocks: text, plus a document (url or base64) or a file id from POST /o/{org}/files. The agent reads the file at /workspace/.rush/attachments/<name>. A URL that cannot be fetched is 422 INPUT_FETCH_FAILED and no run starts. The whole request body is limited to 32 MB when you use base64.

### What if none of the lists match the brief?

The agent does not create a list or a segment. It picks the closest existing audience, says the fit is weak, and names the ones it considered from get_lists and get_segments. You can delete the draft or retarget it. create_list is a real tool on the server and is out of bounds for this prompt.

### Does it work with a read-only Klaviyo key?

Creating a campaign and a template needs write access. The connector catalog says the account needs an Owner, Admin or Manager role. A connection that can only call get_campaigns will fail create_campaign, and that error belongs in the transcript. Use a role that can create drafts, and keep send as a separate step.
