AI writing
Write a native post per network from one idea, without publishing.
POST /api/generate writes one draft per network: each within that network's length, with its own hashtags and format. Nothing is published. Feed drafts back into POST /api/post as per_platform to publish exactly what was written.
AI drafts: a day and a month. Free about 20 a month, up to 5 a day; Starter about 30 a month; Pro about 60 a month; Agency about 150 a month; Scale about 330 a month. An AI draft is one idea written for every network picked; the month is a budget (it renews on the 1st, UTC), so a draft to two networks uses less of it than one to ten. GET /api/ai shows about how many are left. Past the month, AI credit packs are used — about 40 drafts for $5 or about 190 drafts for $20, a one-time payment that does not expire (POST /api/ai/credits/checkout). When both are used up the AI routes answer 429 ai_month_limit with buy_ai_credits links and the plan above; publishing, scheduling and What works never stop.
POST /api/generate: Write a native post per network, without publishingGET /api/ai: AI drafts: about how many this month's AI budget holds and has left, AI credits and the packsPOST /api/ai/credits/checkout: A Stripe Checkout link to buy an AI credit pack (one-time payment)
Write a native post per network, without publishing
/api/generateReturns one draft per requested network. Feed `drafts` straight back into /api/post as `per_platform` to publish exactly what was written. platforms may be an array or a comma-separated string ("tiktok, instagram"); the idea may be sent as prompt, idea, text or topic. With no platforms, the account's connected networks are used. 400 codes: prompt_required, platforms_required, unknown_platform; 502 ai_failed when the writer fails. When the idea asks for a thread ("write a thread about…") and x is one of the networks, the X draft also has thread: ["…"] (the posts after text), ready for per_platform.x.thread; otherwise X gets one post. Same single AI call either way.
Needs an API key: Authorization: Bearer pw_live_….
Body (JSON)
| Field | Type | Description |
|---|---|---|
prompt required | string | The idea to communicate. Up to 4000 characters. |
platforms required | array of string | One of: tiktok, instagram, youtube, linkedin, facebook, bluesky, mastodon, telegram, discord, x. |
media_url | string (uri) | |
brand_voice | string | Up to 1500 characters. |
Responses
| Status | Means |
|---|---|
200 | { drafts: { <platform>: { text } } } |
400 | prompt or platforms[] missing |
429 | ai_daily_limit (the plan's AI drafts a day) or ai_month_limit (the month's AI drafts and any AI credits used up; with buy_ai_credits links and the plan above) |
503 | AI not configured |
curl -X POST "https://postwire.io/api/generate" \
-H "Authorization: Bearer $POSTWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "We now proof our sourdough for 36 hours",
"platforms": [
"tiktok",
"linkedin",
"bluesky"
],
"brand_voice": "warm, specific, no hype"
}'const res = await fetch("https://postwire.io/api/generate", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"prompt": "We now proof our sourdough for 36 hours",
"platforms": [
"tiktok",
"linkedin",
"bluesky"
],
"brand_voice": "warm, specific, no hype"
}),
});
console.log(res.status, await res.json());import os, requests
r = requests.post(
"https://postwire.io/api/generate",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
json={
"prompt": "We now proof our sourdough for 36 hours",
"platforms": ["tiktok", "linkedin", "bluesky"],
"brand_voice": "warm, specific, no hype",
},
)
print(r.status_code, r.json())Example response
{
"drafts": {
"tiktok": {
"text": "36 hours. That's how long this dough rests… #sourdough #bakery"
},
"linkedin": {
"text": "We doubled our proofing time to 36 hours. Three things changed…"
},
"bluesky": {
"text": "Sourdough, now proofed for 36 hours. The crumb is open, the crust cracks. Worth the wait."
}
}
}Texts shortened. Publish them with POST /api/post and per_platform: drafts.
AI drafts: about how many this month's AI budget holds and has left, AI credits and the packs
/api/aiEvery plan includes AI drafts a day (genDay) and an AI budget a month, said in drafts: about 20 on Free, 30 on Starter, 60 on Pro, 150 on Agency, 330 on Scale (an AI draft is one idea written for every network picked; a draft to two networks uses less of the month than one to ten). It renews on the 1st (UTC). Past it, AI credit packs (one-time, no expiry) are used. When both are used up, AI routes answer 429 `ai_month_limit` with `buy_ai_credits` links and the plan above; publishing and scheduling never stop.
Needs an API key: Authorization: Bearer pw_live_….
Responses
| Status | Means |
|---|---|
200 | { month: { drafts_about, drafts_left_about, used_pct, drafts_a_day, resets_on }, credits: { drafts_left_about }, rule, packs: [{ pack, usd, drafts_about }], buy?: [{ pack, usd, drafts_about, url, label }], history: [...], upgrade? } |
curl "https://postwire.io/api/ai" \
-H "Authorization: Bearer $POSTWIRE_API_KEY"const res = await fetch("https://postwire.io/api/ai", {
method: "GET",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}` },
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://postwire.io/api/ai",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
)
print(r.status_code, r.json())Example response
{
"month": {
"drafts_about": 30,
"drafts_left_about": 12,
"used_pct": 0.6,
"drafts_a_day": 60,
"resets_on": "2026-11-01"
},
"credits": {
"drafts_left_about": 40
},
"packs": [
{
"pack": "5",
"usd": 5,
"drafts_about": 40
},
{
"pack": "20",
"usd": 20,
"drafts_about": 190
}
],
"buy": [
{
"pack": "5",
"usd": 5,
"drafts_about": 40,
"url": "https://postwire.io/api/ai/credits/go?t=…&pack=5"
}
]
}Abridged: it also returns rule (the sentence) and history (the AI credit's last movements).
A Stripe Checkout link to buy an AI credit pack (one-time payment)
/api/ai/credits/checkoutPacks: "5" = about 40 AI drafts for $5, "20" = about 190 for $20. Used after the month's AI drafts; they do not expire. Nothing is charged until the person pays on the Stripe page. A refund or dispute takes back what is left of the pack.
Needs an API key: Authorization: Bearer pw_live_….
Body (JSON)
| Field | Type | Description |
|---|---|---|
pack required | string | One of: 5, 20. |
Responses
| Status | Means |
|---|---|
200 | { url } of the Stripe Checkout page |
403 | internal_account (internal accounts are not billed for AI) |
503 | ai_credits_unavailable: packs are not on sale on this server yet |
curl -X POST "https://postwire.io/api/ai/credits/checkout" \
-H "Authorization: Bearer $POSTWIRE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"pack": "5"
}'const res = await fetch("https://postwire.io/api/ai/credits/checkout", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.POSTWIRE_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
"pack": "5"
}),
});
console.log(res.status, await res.json());import os, requests
r = requests.post(
"https://postwire.io/api/ai/credits/checkout",
headers={"Authorization": f"Bearer {os.environ['POSTWIRE_API_KEY']}"},
json={
"pack": "5",
},
)
print(r.status_code, r.json())Example response
{
"url": "https://checkout.stripe.com/c/pay/cs_live_…"
}