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.

Loading…