HomeGuidesAPI Reference
ChangelogHelp CenterCommunityContact Us
Guides

Integrate a forms and popups platform

Learn how to integrate a forms, popups, or list-growth platform with Klaviyo.

Overview

This resource will help you as you build an integration between a forms, popups, or list-growth platform and Klaviyo. The core job of this category is capturing an email or phone number at a form and subscribing that profile to a Klaviyo list with compliant marketing consent. This guide walks through that consent flow in detail, the signup events to send, the profile properties to sync, and the authentication and certification requirements, with an API quick reference at the end.

Why connect your forms and popups platform to Klaviyo

List growth is where the whole lifecycle begins. Every welcome flow, every campaign, every segment starts with a subscriber, and the popup or form is where that subscriber is captured. The brands that grow fastest don't leave that signup stranded in a separate tool. They land it directly in the platform where they run every channel, with consent attached, ready to be messaged. When that platform is Klaviyo, you get measurable wins:

  • Trigger the welcome flow instantly. A new subscriber who lands in Klaviyo with consent enters the merchant's welcome flow the moment they sign up, no export, no delay.
  • Deliver the offer through the merchant's own channel. The coupon or incentive goes out in the welcome flow with the merchant's sender reputation and branding, not a one-off email from your tool.
  • Attribute growth by source. Signup source on the profile lets the merchant see which form or campaign drives the highest-value subscribers.
  • Capture consent correctly, once. Getting consent right at the point of capture is what makes every downstream send compliant and deliverable.
  • Stickier merchants. A form integration that reliably grows a compliant list is one of the strongest retention signals across the partner program.

Short version: a signup at your form becomes a consented Klaviyo subscriber the merchant can message immediately.

The flow

One-way push, partner to Klaviyo. A visitor submits your form; you subscribe them to a Klaviyo list with marketing consent, and optionally emit signup events. Each profile is keyed by email first, with phone where the form collects an SMS opt-in.

The heart of this integration is the subscribe-with-consent call. Everything else (signup events, source properties) is additive. Get the consent flow right first.

Subscribe with consent

There are two endpoints, for two contexts. Choosing the right one is the main architectural decision in this category.

Bulk Subscribe ProfilesCreate Client Subscription
EndpointPOST /api/profile-subscription-bulk-create-jobs/POST /client/subscriptions/?company_id={public_key}
Where it runsServer-side (your backend)Client-side (browser widget JS)
AuthOAuth Bearer tokenPublic API key (the company_id query param). Never a private key.
Best whenYour form submission round-trips through your backend before reaching Klaviyo (most SaaS form and popup apps)Your embed is a pure client-side widget with no backend in the request path
VolumeUp to 1,000 profiles per request; good for batch and backfill tooOne profile per call

Most forms and popup platforms use Bulk Subscribe Profiles even for a single signup (an array of one), because the submission already passes through your backend for validation, dedup, and analytics before it reaches Klaviyo. Use Create Client Subscription only when the embed talks to Klaviyo directly from the browser.

Double opt-in is the merchant's setting, not yours

When you subscribe a profile to a specific list_id, that list's opt-in setting governs what happens. If the list uses double opt-in, the profile enters a pending state and Klaviyo sends its own confirmation message; the profile is not marketing-subscribed until they confirm. If it uses single opt-in, the profile is subscribed immediately.

Respect that setting. Don't build a parallel confirmation experience that duplicates Klaviyo's opt-in email or SMS, and don't use the historical_import flag to bypass double opt-in for live signups. That flag is only for genuine backfills of subscribers who already have timestamped consent.

Capture consent compliantly

Consent must be explicit, especially for SMS:

  • Show clear disclosure language at the point of capture (for example: "By submitting, you agree to receive marketing emails from {Merchant}." For SMS: "Consent is not a condition of purchase. Msg & data rates may apply.").
  • Keep SMS consent separate from email consent in the UI. Don't bundle both under one silent, pre-checked box.
  • Only set consent to subscribed when the visitor took an explicit action. Never default or infer it.
  • When backfilling, store the real timestamp of the on-page consent action, not the time of the API call.

See Collect email and SMS consent via API for the full compliance guidance.

Events to send (optional)

Beyond the subscribe call, emit signup events so merchants can measure form performance and personalize follow-ups. Event names are not prefixed with your platform name; branded events handle source attribution via your OAuth token. Use Title Case payload field names.

EventWhen it firesPayload
Popup DisplayedThe popup or embed renders for a visitor.Form ID, Form Name, Trigger (exit intent / time delay / scroll / page load), Page URL
Form ViewedThe visitor engages with or expands the form (for multi-step forms).Form ID, Form Name, Step
Offer RevealedA coupon or incentive is shown or unlocked (e.g., spin-to-win).Offer Type (coupon / free shipping / gift), Offer Value, Coupon Code
Form SubmittedThe visitor submits the form (may precede confirmation on double opt-in).Form ID, Form Name, Signup Source, Fields Submitted
Signed UpThe profile is confirmed subscribed (post-confirmation, or immediately on single opt-in).Signup Source, List ID, Consent Channel (email / sms / both)

$value guidance: signup events don't carry revenue. Don't set $value.

Profile properties to sync (optional)

Use Title Case property names prefixed with your platform's name. Replace {Platform} below with the literal name of your platform (e.g., "Acme Signup Source"). These let merchants segment and attribute by where a subscriber came from.

PropertyWhat it is
{Platform} Signup SourceThe specific form, popup, or campaign the profile came from.
{Platform} Form NameHuman-readable name of the form or popup.
{Platform} Offer ShownThe coupon or incentive presented at signup.
{Platform} Coupon CodeThe actual code issued, if any.
{Platform} Signup Page URLThe page the visitor was on when they converted.

Implementation notes:

  • Pick property names up front and don't rename them. Renames break every segment, flow, and template that references them.
  • Send phone numbers in E.164 format (for example +15005550101), with no spaces or punctuation. Invalid numbers are dropped from a bulk job while the rest of the batch succeeds, so check for partial failures rather than assuming all-or-nothing.
  • Let the merchant choose the target list per form in your settings UI rather than hardcoding one list. Multi-brand and multi-language merchants need per-form list targeting.

Auth, scopes, and certification

Build as a public OAuth app for server-side calls. Private API keys are a non-starter for the marketplace. Start with the Create a public OAuth app guide.

Client-side calls are the exception: Create Client Subscription authenticates with the merchant's public API key passed as company_id, not an OAuth token. The public key is safe to expose in browser JavaScript by design. Never put a private key in the browser, and never call the client endpoint from your server.

Key OAuth specifics for forms integrations:

  • PKCE is required on the authorization code exchange (code_verifier must be included).
  • Access tokens are short-lived (1 hour). Refresh tokens are long-lived but rotate on every use. Persist the latest refresh token after every refresh call.
  • Tokens are scoped per Klaviyo account. Merchants with multiple Klaviyo accounts install separately for each.

Scopes to request (server-side OAuth):

  • subscriptions:write and profiles:write to subscribe profiles with consent.
  • lists:read to populate the list picker in your settings UI so merchants choose the target list.
  • events:write only if you emit the optional signup events above.
  • lists:write only if your integration actually creates lists. Don't request it just to subscribe to an existing one.

Don't over-scope. Unused scopes get flagged in marketplace review.

Once functional, submit for App Marketplace review under the forms and popups (list growth) category. Plan for at least one round of feedback. The review covers OAuth, scopes, install and uninstall validation, consent handling, and that the integration delivers what your listing claims.

API quick reference

Concrete specs for AI coding assistants and engineers grounding on this doc. The narrative above is the why and the flow. This section is the what-to-actually-type.

Headers (required on every server-side request)

Authorization: Bearer <oauth_access_token>
revision: 2026-04-15
Accept: application/json
Content-Type: application/json

Pin the revision header to a specific date when you ship. Check the API versioning guide for the current latest. Without it you'll get a 400.

Subscribe with consent (server-side)

Subscribe a profile to a list with email and SMS marketing consent. The target list is passed as a relationship; the list's opt-in setting governs single versus double opt-in.

POST /api/profile-subscription-bulk-create-jobs/

{
  "data": {
    "type": "profile-subscription-bulk-create-job",
    "attributes": {
      "profiles": {
        "data": [
          {
            "type": "profile",
            "attributes": {
              "email": "[email protected]",
              "phone_number": "+15005550101",
              "subscriptions": {
                "email": { "marketing": { "consent": "SUBSCRIBED" } },
                "sms": { "marketing": { "consent": "SUBSCRIBED" } }
              },
              "properties": {
                "{Platform} Signup Source": "exit-intent popup",
                "{Platform} Coupon Code": "WELCOME10"
              }
            }
          }
        ]
      },
      "historical_import": false
    },
    "relationships": {
      "list": {
        "data": { "type": "list", "id": "Y6nRLr" }
      }
    }
  }
}

Set historical_import to true only for genuine backfills of already-consented subscribers with real historical timestamps, never for live signups. Verify the exact request shape against the Bulk Subscribe Profiles reference before you ship, as the JSON:API envelope can change between revisions.

Subscribe with consent (client-side)

For a pure browser widget with no backend, post to the client endpoint with the merchant's public key. Send the same subscriptions object; consult the Create Client Subscription reference for the exact client envelope.

POST /client/subscriptions/?company_id=PUBLIC_KEY

Signup event POST body

POST /api/events/

{
  "data": {
    "type": "event",
    "attributes": {
      "properties": {
        "Form Name": "Exit Intent 10% Off",
        "Signup Source": "exit-intent popup",
        "Consent Channel": "both"
      },
      "time": "2026-06-17T14:22:11Z",
      "unique_id": "signup_7f31a9",
      "metric": {
        "data": {
          "type": "metric",
          "attributes": { "name": "Signed Up" }
        }
      },
      "profile": {
        "data": {
          "type": "profile",
          "attributes": { "email": "[email protected]" }
        }
      }
    }
  }
}

Include time (when the event occurred, ISO 8601) and unique_id (a stable identifier for the event, such as your submission ID) on every event. Klaviyo uses unique_id to deduplicate.

Retries

  • 429: respect the Retry-After header. Otherwise exponential backoff starting at 1s, cap at 60s, max 5 retries.
  • 5xx: same backoff.
  • 4xx (other than 429): don't retry. Log and surface to the merchant.

Consent and identifier guidance

  • Only set consent: "SUBSCRIBED" when the visitor took an explicit action. Never pre-check or infer consent, and keep SMS consent separate from email.
  • Identify by email first, phone second. Send phone in E.164 format.
  • Respect the target list's double opt-in setting; don't build a parallel confirmation flow.

Rate limits

Per-account, not per-app. Bulk Subscribe Profiles handles up to 1,000 profiles per request, which covers both live signups and batch backfills comfortably. For merchants at unusual scale, request per-account increases through Klaviyo Support and CSM before launch.

Glossary

  • Klaviyo profile: customer record. Keyed by email, phone, or external ID.
  • Klaviyo list: a static audience, and the consent surface. Subscribing a profile to a list with marketing consent is what makes them messageable.
  • Marketing consent: explicit permission to send marketing email or SMS. Captured at the form and passed on the subscribe call.
  • Double opt-in: a list setting where a subscriber must confirm before they're marketing-subscribed. Controlled by the merchant on the list, not by your integration.
  • Single opt-in: a list setting where a subscriber is subscribed immediately on the subscribe call.
  • Public API key (company_id): the site ID used to authenticate client-side calls from the browser. Safe to expose; never use a private key client-side.
  • Signup source: which form or campaign a subscriber came from, stored as a profile property for attribution.

Resources


Did this page help you?