Write an email
Most emails don't need creativity, they need to exist: a clear ask, the right tone, sent before the moment passes. This endpoint takes a short description of what you want to say and who you're saying it to, and returns a complete, sendable email so the blank draft never becomes the bottleneck.
Run it online
Run this on our servers with your account. Free tools run in your browser; this one bills your KIT balance per the price above.
For the message you know you have to send but keep postponing
Support agents, sales reps, ops teams and founders all sit on a queue of emails that are simple in substance but tedious to phrase well every single time: a follow-up, a status update, a polite no. This endpoint is built for exactly that gap between knowing what to say and having it written cleanly, taking a short intent plus recipient context and returning a full email rather than a fragment you still have to assemble.
What goes in and what comes out
A request includes the intent, some context about the recipient or situation, and a desired tone, formal, warm, brief, whatever fits. The response is a complete email with subject line and body, matched to that tone. Because generation is asynchronous, the call returns a task_id immediately, and the finished email arrives by signed webhook or through a signed link that stays valid for 24 hours, ready to drop into an outbox, a CRM field or a support ticket.
Why tone matching matters more than word count
Business email settled into a handful of recognizable registers long before AI writing existed, a clipped internal update reads nothing like a first outreach to a prospect, and readers notice instantly when the register is wrong for the relationship. A technically correct email in the wrong tone still reads as off, sometimes worse than one with a small grammar slip. That's why tone is a first-class input here rather than an afterthought applied by rewriting after the fact.
Where this sits inside a bigger workflow
This is typically the last step after a CRM trigger, a support ticket update or a form submission decides that an email needs to go out; the endpoint fills in the actual words. Pricing is a small base fee plus a per-email charge, so composing one message costs about the same each time regardless of volume, and a failed generation is never billed, since the task retries automatically up to three times before returning a clear error instead of a charge.
The line it doesn't cross
The output is a strong draft built from what you provide, not a source of new facts, so anything specific, a price, a date, a commitment, should come from your own data rather than be invented by the model. Message content is deleted after the retention window and never used to train models, which matters when the email concerns an unannounced deal or a sensitive account situation.
What you can do with it
Support follow-up at volume
A support team triggers an email per resolved ticket, each one composed from the ticket's resolution notes and matched to a warm, reassuring tone.
Sales outreach personalization
A rep supplies a prospect's role and a specific pain point pulled from a call, and gets a first-touch email tailored to that context instead of a generic template.
Internal status updates
A project lead turns a bullet list of progress notes into a clear, brief status email sent to stakeholders every Friday.
Polite decline or delay
An ops team automates the awkward but common email that says no or not yet, keeping the tone respectful without someone drafting it manually each time.
FAQ
How does the email writer API turn an intent into a full email?
You describe what you want to say and give recipient context and a tone, and the endpoint returns a complete email with subject and body matched to that tone.
Is there a free version of the email writer API?
There's no free tier — free tiers get abused and slow everyone down. Access runs on a prepaid ForHosting KIT balance: top up from $10.00 (it never expires) and each request is charged at its published price, so a call with no balance returns HTTP 402. No subscription, no tokens, no invented credits, and a failed task is never charged.
What does it cost per email?
$0.003 per request plus $0.0135 per email composed, a small and predictable cost regardless of how long the email ends up being.
How do I get the finished email?
By signed webhook for production integrations, or from a signed link valid for 24 hours after the task completes.
Can I control the tone of the email?
Yes, tone is a request parameter, so the same intent can be composed as formal, warm or brief depending on the relationship and context.
What happens if a generation fails?
It retries automatically up to three times, and if it still fails you receive a clear error with no charge for the attempt.
Will the API invent facts like prices or dates?
It composes from what you provide rather than inventing specifics, so any concrete price, date or commitment should come from your own data in the request.
Is email content kept after the task finishes?
No, content is deleted after the retention window and never used to train models, which matters for sensitive or unannounced situations.
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, and soon from our app, email and Telegram.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/text/email-compose \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"input":"…"}'const res = await fetch("https://api.kit.forhosting.com/text/email-compose", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"input": "…"
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/text/email-compose",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"input": "…"
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/text/email-compose", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"input":"…"}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"input":"…"}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/text/email-compose", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"input": "…"
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "text.email_compose",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_tokens | 20000 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
422 | task_failed | The task failed after 3 retries. You are never charged for it. |