Add newsletter signup to a site
Any site can collect newsletter subscribers through the Inkform platform's public
subscribe API — no database, no email provider, no server code of your own required.
This guide covers the framework's <SubscribeForm /> component, which is what the
platform itself sends over the wire; if your site isn't on @inkform/framework yet,
skip to Without the framework and call the API directly.
This is one guide for every site. Follow it the same way whether you're integrating mlnotes, the blog, or a brand-new project — the API contract never changes per site.
1. Get a project ID
Every subscriber is scoped to a project in the Inkform dashboard. Create (or open) your site's project there and copy its project ID — you'll need it below.
2. Install the framework
npm install @inkform/framework3. Drop in the form
import { SubscribeForm } from '@inkform/framework/subscribe-form';
import '@inkform/framework/styles.css';
<SubscribeForm
projectId={process.env.NEXT_PUBLIC_INKFORM_PROJECT_ID}
apiBaseUrl={process.env.NEXT_PUBLIC_INKFORM_PLATFORM_URL ?? 'https://api.inkform.dev'}
/>apiBaseUrl must be the platform's API origin — https://api.inkform.dev,
not the apex. The platform routes by host, and only api. serves
/api/newsletter/*. A POST to https://inkform.dev/api/newsletter/subscribe
gets a 308 to www.inkform.dev, which 307s on to app.inkform.dev; since
redirect responses carry no CORS headers, the browser's fetch fails on the
chain instead of subscribing anyone.
The endpoint is CORS-open, so this works from any domain. Set both as env vars so the form can be left out entirely (render it conditionally) until the project ID actually exists:
# .env.local
NEXT_PUBLIC_INKFORM_PROJECT_ID=
NEXT_PUBLIC_INKFORM_PLATFORM_URL=https://api.inkform.devPrefer an MDX shortcut inside docs content? Register NewsletterCTA in your
mdx-components.tsx (see this site's own mdx-components.tsx for the pattern) and use:
<NewsletterCTA heading="Subscribe for updates" />What happens after submit
- The form
POSTs{ projectId, email, source }to/api/newsletter/subscribe. - Inkform creates a pending subscriber and emails a confirmation link (double opt-in — required before any campaign send).
- The subscriber clicks the link, flips to active, and starts receiving campaigns sent from the dashboard for that project.
That's the whole integration. Sending, unsubscribe links, open/click tracking, and bounce handling are all handled on the Inkform side — your site never touches subscriber data or Resend directly.
Without the framework
Not using @inkform/framework? POST directly:
await fetch('https://api.inkform.dev/api/newsletter/subscribe', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ projectId, email, source: 'site' }),
});A 200 means the subscriber was created or already existed; a 4xx body carries
{ error } — e.g. an invalid email or an unknown projectId. A 429 means a
rate limit tripped (per IP, per address, or per project); it carries a
Retry-After header — surface "try again shortly" rather than retrying in a loop.
Signing up the same address repeatedly won't re-send the confirmation email: one confirmation per address per 15 minutes, however many times it's requested. The response is identical either way, so a repeated signup still looks like a success to the visitor.