Mailer API
Documentation for integrating HTML Refresher with the mailing system.
Overview
HTML Refresher turns a saved campaign into a fresh, unique prompt every time it's requested. There are two ways to build a campaign in the UI:
- Generate New — paste a complete master prompt (brand rules, structure, and variation instructions). No reference HTML is needed; the prompt already tells the LLM how to vary colors, layout, and copy on its own each run.
- Refresh Existing — paste an existing HTML email creative. The server rebuilds a fresh rewrite prompt from the saved reference and options on every call.
Either way, the mailer's job is the same: save a campaign once through the HTML Refresher UI, then pull its prompt from this API on every send, run it against an LLM, and deliver the returned HTML.
Every response includes the LLM provider, model, and temperature that produced the HTML Refresher preview. Running the prompt through those exact settings gets the closest possible match to what was reviewed and approved.
https://htmlrefresher.vibelogicllc.com
Authentication
Every protected endpoint requires an X-API-Key header. Requests missing it receive 401 Unauthorized.
hr_8ee3224ca90950d120a57c09d63be0c3
X-API-Key: hr_8ee3224ca90950d120a57c09d63be0c3
Generate variation from a saved campaign
The simplest call in the API. Pass a saved campaign ID and get back a freshly worded prompt every time — no parameters needed. This works for both campaign types:
- For a Generate New campaign, the response is the saved master prompt returned verbatim — since the prompt itself instructs the LLM to randomize colors, layout, and copy every run, no server-side rewriting is needed.
- For a Refresh Existing campaign, the server rebuilds the rewrite prompt from the saved reference creative and options on every call, so the wording is never identical between sends.
Request
GET /api/library/1774275391429-h3oze5/generate
X-API-Key: hr_8ee3224ca90950d120a57c09d63be0c3
No request body needed. The ID comes from GET /api/library — use the id field on any saved item.
Response
{
"id": "1774275391429-h3oze5",
"title": "Lowe's · EGO Blower Survey Refresh",
"prompt": "=== REFRESH CREATIVE REQUEST ===\nBrand: Lowe's\n...",
"llm": {
"provider": "openai",
"model": "gpt-5.4",
"temperature": 1.5,
"maxTokens": 4000
}
}
JavaScript example
const res = await fetch(
"https://htmlrefresher.vibelogicllc.com/api/library/1774275391429-h3oze5/generate",
{ headers: { "X-API-Key": "hr_8ee3224ca90950d120a57c09d63be0c3" } }
);
const data = await res.json();
// data.prompt → send to LLM as user message
// data.llm → use model + temperature for the LLM call
curl example
curl https://htmlrefresher.vibelogicllc.com/api/library/1774275391429-h3oze5/generate \
-H "X-API-Key: hr_8ee3224ca90950d120a57c09d63be0c3"
PHP example
$data = json_decode(file_get_contents(
"https://htmlrefresher.vibelogicllc.com/api/library/1774275391429-h3oze5/generate",
false,
stream_context_create(["http" => [
"header" => "X-API-Key: hr_8ee3224ca90950d120a57c09d63be0c3"
]])
), true);
$prompt = $data["prompt"]; // send to LLM
$model = $data["llm"]["model"]; // "gpt-5.4"
$temp = $data["llm"]["temperature"]; // 1.5
Library pull — all items
Returns all prompts saved through the HTML Refresher UI — curated, named, and approved.
Request
GET /api/library
X-API-Key: hr_8ee3224ca90950d120a57c09d63be0c3
Response
An array of library items. Each item has this shape:
{
"id": "1743462240123-ab8c4f",
"title": "Costco Meat Box · June send",
"createdAt": "3/20/2026, 2:14:00 PM",
"prompt": "=== Core brief ===\nCreate a fresh HTML email...",
"llm": {
"provider": "openai",
"model": "gpt-5.4",
"temperature": 1.5,
"maxTokens": 4000
},
"state": { }
}
state field contains the HTML Refresher form state — the mailer can ignore it. Only prompt and llm are needed. If llm is missing (items saved before the API was added), fall back to GET /api/settings/llm.
Library pull — single item
Fetches one saved prompt by its ID. Useful when the mailer stores a specific item ID to pull on send.
GET /api/library/1743462240123-ab8c4f
X-API-Key: hr_8ee3224ca90950d120a57c09d63be0c3
Returns a single item object in the same shape as above, or 404 if the ID does not exist.
Default LLM settings
Returns the server's currently configured default LLM settings. No authentication required. Use this as a fallback when a library item is missing its llm field.
GET /api/settings/llm
Response
{
"openai": {
"model": "gpt-5.4",
"temperature": 1.5,
"maxTokens": 4000
},
"deepseek": {
"model": "deepseek-chat",
"temperature": 1.5,
"maxTokens": 4000
}
}
Running the prompt through the LLM
The prompt is a plain text string. Send it as a single user message — no system prompt is needed. All instructions are embedded inside the prompt itself.
OpenAI example
POST https://api.openai.com/v1/chat/completions
Authorization: Bearer YOUR_OPENAI_KEY
Content-Type: application/json
{
"model": "gpt-5.4",
"temperature": 1.5,
"max_completion_tokens": 4000,
"messages": [
{ "role": "user", "content": "<full prompt string from API>" }
]
}
DeepSeek example
POST https://api.deepseek.com/chat/completions
Authorization: Bearer YOUR_DEEPSEEK_KEY
Content-Type: application/json
{
"model": "deepseek-chat",
"temperature": 1.5,
"max_tokens": 4000,
"messages": [
{ "role": "user", "content": "<full prompt string from API>" }
]
}
max_completion_tokens; DeepSeek uses max_tokens.
The [LINK] token
Every prompt instructs the LLM to use [LINK] as the sole href in the email.
After the LLM returns HTML, do a simple string replace before delivering:
const html = llmResponse.replace(/\[LINK\]/g, "https://your-send-url.com/campaign123");
If you pass a custom linkToken in the request (e.g. "{{SEND_URL}}"), replace that token instead.
Getting a fresh variant every call
GET /api/library/:id/generate re-runs the refresh prompt builder against the saved reference
creative on every call — there is no seed to manage. Just call the endpoint again on each send and a
newly worded rewrite prompt comes back every time.
model and temperature from the llm block. LLMs are non-deterministic even at the same temperature, so output won't be pixel-identical — but the brand, layout, and copy tone stay consistent because those are locked in by the prompt text and the saved reference creative.
Error responses
All errors return JSON:
{ "error": "Human-readable error message." }
| Status | Meaning |
|---|---|
| 400 | Bad request — missing or malformed body |
| 401 | Missing or invalid X-API-Key |
| 404 | Library item not found |
| 405 | Wrong HTTP method for this endpoint |
| 500 | Server error |
Quick reference
| Endpoint | Method | Auth | Purpose |
|---|---|---|---|
| /api/library/:id/generate | GET | Yes | Recommended. Fresh prompt from a saved campaign (Generate New or Refresh Existing) — just the ID, no params |
| /api/library | GET | No | List all saved campaigns and their IDs |
| /api/library/:id | GET | No | Fetch one saved campaign by ID |
| /api/settings/llm | GET | No | Default LLM config (fallback) |
HTML Refresher — htmlrefresher.vibelogicllc.com