Skip to main content
One request, one email. The call returns a Server-Sent Events stream rather than a single JSON body, because generation takes a minute or three and an open stream is what keeps proxies from dropping the connection while it works.

Request

prompt carries the result. Say who it’s going to, what you want them to do, and the tone — the same detail you’d give a colleague. branding_url then handles the look without you describing it.

Response

Authentication, limits, and quota are checked before the stream opens, so a rejected request is an ordinary JSON error with a 4xx status. Once accepted you get Content-Type: text/event-stream and these events: Exactly one result or one error arrives, then the stream closes.

The result event

A few fields worth knowing about:
html is present when format is html or both; mjml when it’s mjml or both. Ask for both if you want to store the editable source alongside what you send.
composite is the overall quality score, 0–100, and structural and content are its two main parts. dimensions holds each measured dimension by name, and which ones appear can vary. visual and branding are always null today; they stay in the shape for compatibility. Scoring also runs on a timeout, and if it doesn’t finish, score itself is null. A missing score isn’t a bad score, so never treat null as 0.
The first entry matches the top-level subject and preheader. The others are alternatives, some carrying a predicted open-rate bucket and a suggested split.
email_id refers to a real email in AlpacaRelay, not just a response identifier. You can open and keep editing it in the app.

Try it with curl

Use -N (--no-buffer). Without it curl buffers the response and you’ll sit watching nothing until generation finishes.
Output looks like this:

Node example

The stream is plain text, so no SSE library is needed — but you do have to buffer, because an event can arrive split across chunks:
Set a client timeout of at least six minutes. Generation usually takes one to three, but the server’s own backstop fires later. Disconnecting doesn’t stop the generation: it still finishes, is charged, and is saved in your workspace. You just don’t receive it on the stream.

What to expect

  • Generation takes roughly 1–3 minutes. Treat it as a background job, not a request you block a user on.
  • A credit is taken when generation starts, and refunded if the generation itself fails. See Credits and limits.
  • Three concurrent generations per workspace, and 60 calls a minute per key. Going over either one is rejected with rate_limited before the stream opens.

Next

Errors

Every code the endpoint can return, and what to do about each.

Credits and limits

What a generation costs and how much you can run at once.