Newsletter signup form6 min read
Send your own newsletter form's signups to beehiiv
Connect your own signup form to beehiiv: a relay creates each subscription with double opt-in on, the placement as UTM source and the page as referrer.
Post your own form to Ikiform and point the webhook at a relay that calls beehiiv's create subscription endpoint. Send double_opt_override: "on" so beehiiv emails the confirmation, pass the form's placement as utm_source and the page as referring_site, and keep reactivate_existing false so a signup never quietly undoes an unsubscribe. One API call per signup, with the key kept in the relay.
What's different with beehiiv
beehiiv's API is organised around publications. A subscription is an email address subscribed to one publication, and you create it with one POST to /v2/publications/{publicationId}/subscriptions. Everything about the signup travels in that one request: whether to confirm, whether to send the welcome email, where the reader came from, and custom fields. There's no separate tag or form step as in Kit.
beehiiv also reports acquisition in UTM terms. Its subscription records carry utm_source, utm_medium, utm_campaign and referring_site. Map your form's fields onto those, and each subscriber in beehiiv shows which placement and which article brought them in:
- fields.email becomes email.
- fields.source becomes utm_source.
- meta.referrer becomes referring_site.
- fields.first_name becomes custom_fields: First Name.
- double_opt_override: "on" is added by the relay.
- reactivate_existing: false is added by the relay.
Set up beehiiv
- In beehiiv, open Settings, then API under Workspace Settings, and choose Create New API Key. Copy it right away; beehiiv won't show it again.
- On a paid plan, restrict the key to the one publication the form feeds. A publication-scoped key gets
404for any other publication, which limits the damage if it leaks. - Find the publication's id, the prefixed id beehiiv's API uses in the path. The API's list publications endpoint returns it.
- Create a custom field named
First Nameif you want to store first names. beehiiv discards custom fields that don't exist yet. - Set up the double opt-in and welcome emails in the publication's settings. The relay turns them on per request; their content lives in beehiiv.
The relay
This is a Netlify Function, which suits a site already on Netlify: it runs on your own domain, next to the static site, with no separate service. Install @netlify/functions for the types, and set the three variables in the site's environment settings.
import type { Config } from "@netlify/functions";
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";
export default async (request: Request) => {
const payload = await request.text();
let event;
try {
event = await verifyWebhook({
payload,
headers: request.headers,
secret: Netlify.env.get("IKIFORM_WEBHOOK_SECRET")!,
});
} catch (error) {
if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
throw error;
}
if (event.type !== "submission.created") return new Response(null, { status: 204 });
const { email, first_name, consent, source } = event.data.fields;
if (consent !== true || typeof email !== "string") return new Response("Skipped", { status: 200 });
const publication = Netlify.env.get("BEEHIIV_PUBLICATION_ID");
const answer = await fetch(`https://api.beehiiv.com/v2/publications/${publication}/subscriptions`, {
method: "POST",
headers: {
Authorization: `Bearer ${Netlify.env.get("BEEHIIV_API_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
double_opt_override: "on",
send_welcome_email: true,
reactivate_existing: false,
utm_source: typeof source === "string" ? source : "website",
utm_medium: "signup_form",
referring_site: event.data.meta.referrer ?? undefined,
custom_fields: typeof first_name === "string" && first_name ? [{ name: "First Name", value: first_name }] : [],
}),
});
if (answer.status === 429) {
const reset = Number(answer.headers.get("ratelimit-reset"));
const wait = reset ? Math.max(1, reset - Math.floor(Date.now() / 1000)) : 60;
return new Response("beehiiv rate limit", { status: 503, headers: { "retry-after": String(wait) } });
}
if (!answer.ok) return new Response(`beehiiv answered ${answer.status}: ${await answer.text()}`, { status: 502 });
return new Response(null, { status: 204 });
};
export const config: Config = {
path: "/hooks/beehiiv",
};double_opt_override: "on"sends the confirmation email whatever the publication's default is. Use"not_set"to follow the publication setting instead, and never"off"for a public form.send_welcome_emaildefaults tofalsein the API. Set it totrueif you want API signups to get the welcome email that embed signups get.reactivate_existing: falseis the default too. Spell it out so nobody flips it later without thinking; it reactivates people who unsubscribed.- beehiiv answers
200with the subscription, and itsstatusstarts asvalidatingwhile beehiiv checks the address, thenpendinguntil the reader confirms. - On
429, the relay reads beehiiv'sRateLimit-Reset, a Unix time, and turns it into aRetry-Afterdelay for Ikiform.
The function's URL is https://your-site.com/hooks/beehiiv. Add it under Webhooks in Ikiform and copy the whsec_ secret into IKIFORM_WEBHOOK_SECRET. The webhooks docs list the URL rules, and your own server shows how to skip a delivery you've already handled.
Choosing UTM values
beehiiv's UTM fields are free text, so decide the values once and keep them consistent, or the records split into near-duplicates such as footer and Footer. The form's hidden source input is the natural utm_source. Keep utm_medium fixed, so every signup from your own forms can be told apart from beehiiv's hosted pages and from imports:
| Placement | source input | utm_source | utm_medium |
|---|---|---|---|
| Site footer | footer | footer | signup_form |
| End of a post | post | post | signup_form |
| Newsletter page | newsletter-page | newsletter-page | signup_form |
If you run a campaign that links to a page with the form, read the campaign from the page URL in the relay. meta.referrer holds the full URL of the page, query string included when the browser sends it, so the relay can copy a utm_campaign parameter from it into the request.
Rate limits by plan
beehiiv measures its limit per API key in fixed 60-second windows, and the limit depends on the plan. Each signup is one request:
| beehiiv plan | Requests per minute |
|---|---|
| Free | 30 |
| Lite | 180 |
| Pro | 500 |
| Enterprise | 800, higher on request |
Thirty a minute is enough for most footer forms, but a launch post on a free publication can pass it. The relay's 503 makes Ikiform wait and retry rather than drop the signup. Plan names and limits change, so check beehiiv's rate limiting page if yours isn't listed.
Gotchas
- Custom field names are exact. beehiiv matches custom fields by name and drops unknown ones with a warning in the response, not an error. A typo means first names quietly go missing.
referring_sitecan be empty.meta.referrerisnullwhen the browser didn't send a referrer, and the relay leaves the field out.- Existing subscribers. beehiiv's docs say the call creates or updates the subscription, but not what changes for an active subscriber. Test with your own confirmed address before you put the form on every page.
- Keys are workspace-wide by default. A key without a publication restriction can reach every publication in the workspace. Scope it.
- Paid tiers stay in beehiiv. Leave
tierout so signups join without a paid subscription. Upgrades happen in beehiiv's own checkout.
Check it works
- Choose Send test on the endpoint in Ikiform. The delivery shows
204and beehiiv gets no request. - Subscribe from your page with an address you own. The delivery shows
204. - In beehiiv, the subscriber appears as pending, with your placement as UTM source and the article as referring site.
- The double opt-in email arrives. Confirm it, and the welcome email follows.
- Unsubscribe that address in beehiiv and sign up again. The subscriber stays inactive, because
reactivate_existingis false.
Questions
Yes. Post your form to Ikiform and let a relay call beehiiv's create subscription endpoint for each signup. Your form keeps your design, and beehiiv still confirms, welcomes and emails the reader.
beehiiv's pricing page lists API access on every plan, and its rate limiting page gives its lowest plan 30 requests a minute. The Send API is limited to higher plans, but creating subscriptions doesn't use it.
Send double_opt_override set to "on" in the create subscription request, or "not_set" to use the publication's own setting. "off" makes the subscriber active immediately with no email.
Yes. beehiiv works with Zapier and Make, which follow its rate limits for you. Point the Ikiform webhook at the automation and use its beehiiv action to add a subscriber.
Stuck on a step, or want a guide for your situation? Email hello@ikiform.com.