PostWave Demo · Features
Everything you ship with.
One Next.js deploy, one Postgres, one runtime. The features below are built in — no pricing tiers, no addons. BYOK for AI providers; you ship what your customers see.
01 · AI editor
Inline drafting that sounds like you.
Write longer posts faster. Highlight any paragraph, ask the inline AI to rewrite, expand, condense, or change tone — all under your own provider key. The voice profile drives the prompt so output stays in your register, not the model's defaults.
- BYOK — Anthropic, OpenAI, Google, z.ai. No master key on our side.
- Per-paragraph rewrite, expand, condense, and tone shift.
- Markdown editor with frontmatter, slug auto-generation, cover picker (Unsplash, S3, local).
// packages/ai — every provider implements the same shape.
import { getProvider, DEFAULT_MODELS } from "@postwave/ai";
const adapter = getProvider("anthropic");
const out = await adapter.generate({
apiKey, // user's own key (BYOK)
model: DEFAULT_MODELS.anthropic,
maxTokens: 4096,
system: voiceProfile.system, // tone + register
user: prompt,
});02 · Multi-platform distribution
One post → five platform-native artifacts.
Press Generate. The same draft becomes a LinkedIn carousel PDF, a LinkedIn text post, an X thread, an Instagram carousel ZIP, and an Instagram story image — each respecting platform rules (character limits, slide counts, hashtag etiquette). Schedule for later or fire on demand.
- Five built-in adapters: linkedin-carousel, linkedin-text, x-thread, instagram-carousel, instagram-story.
- Schedule per-post with a cron-driven runner (Vercel Cron / Render / EasyCron).
- Add a new platform with one entry in the adapter registry — no per-platform branching elsewhere.
// lib/distribution/registry.ts — single source of truth.
export const PLATFORM_ADAPTERS = {
"linkedin-carousel": {
outputFormat: "carousel",
prompt: buildCarouselPrompt,
parse: parseCarousel,
download: renderCarouselPDF, // → application/pdf
},
"x-thread": {
outputFormat: "text",
prompt: buildThreadPrompt,
parse: parseThread,
download: renderThreadText, // → text/plain
},
// …add new platforms by extending this map.
};03 · Voice profiles
Reusable tone, taught by example.
Define how you write once: a system rules block, a few example posts, per-platform overrides. Every AI call — inline rewrite, distribution, digest — pulls from the same profile. Switch profiles per post when the brand demands it.
// Each post can pick a voice profile, but most use the default.
const voice = await loadVoice(userId);
// The voice flows into every prompt:
const common = {
postTitle: post.title,
postBody: post.body,
voice,
templateRules: tpl.rules,
};04 · Privacy-first analytics
Numbers without trackers.
First-party page-view + reading-depth analytics, no cookies, no third-party scripts. Hashed visitor identifiers rotate daily. Web Vitals captured for every reader. Fully visible at /admin/analytics.
- Page views, unique visitors, scroll depth, average reading duration.
- Per-post and aggregate views, top referrers, top categories.
- Web Vitals (LCP, INP, CLS) recorded via the navigator.sendBeacon API.
05 · Newsletter
Double opt-in, sendable, themed.
Subscribers confirm via email, unsubscribe with a one-click signed token. Compose digests in admin, preview before send, queue from any post or batch. Email transport pluggable — SMTP today, third-party mailers as a swap.
// Confirmation + unsubscribe links use signed JWT tokens, so the
// link itself is the credential. No email, no DB lookup at click-time.
const token = await new SignJWT({ sub: subscriberId, scope: "unsub" })
.setProtectedHeader({ alg: "HS256" })
.setExpirationTime("90d")
.sign(secret);06 · Annotations
Marginalia for the modern reader.
Readers can highlight any passage and leave an annotation visible to the author. Threaded discussion in the margin, notification dot in admin, batch-archive when read. Disable per-post or globally in settings.
07 · Distribution scheduling
Set it, hit cron, walk away.
Pick a future time when generating distributions. Rows park at status=scheduled with the chosen provider. A single cron call fires due rows in batch — group by post + provider to amortize round-trips.
// vercel.json — minute-resolution cron firing the dispatcher.
{
"crons": [
{
"path": "/api/cron/distributions",
"schedule": "* * * * *"
}
]
}Get started
Read the docs.
Install in under five minutes, point Prisma at your database, drop in your AI provider key, and start writing.