TEST offer emails
View .mdA TEST welcome offer can award a configured perk and send one real email with
[STAGING] at the start of its normal subject. This is an early-access operation:
use a matching API deployment and immutable SDK release that includes these helpers.
Source tests alone do not establish deployed availability or inbox delivery.
Configure the offer
Section titled “Configure the offer”The collection must have a canonical welcome perk with authored email copy and an
immediate lead.captured → DIRECT_ISSUE award rule. DRAFT perks can be rehearsed
in TEST without enabling a production offer. The award-email renderer consumes
an already-issued code, its expiry, approved redemption URL and terms. It does
not invent a booking, join a membership, or issue another code while rendering.
Redemption links
Section titled “Redemption links”With Kismet-tracked offer links enabled by your API deployment, the email’s
Use your offer button goes through an HTTPS Kismet redirect. Your application
continues to use server.offers.capture; it does not construct tracking links,
send coupons to a tracking vendor, or record clicks itself. No additional SDK
method or permission is needed.
In the offer’s private email configuration, set redemptionUrl to the approved
destination for that environment. If the destination accepts a coupon in its
query string, set redemptionCodeParameter to that parameter’s name—for example,
coupon. Kismet inserts the actual issued code with URL-safe encoding and replaces
any existing value for that parameter. Leave it unset for destinations where
guests enter their code manually. Other query parameters remain intact.
Use a TEST provider destination for TEST offers, not a production checkout or a sample coupon. A provider page that drops the coupon during navigation still needs to be corrected by that provider; a redirect cannot preserve parameters across subsequent provider navigation.
Tracked links expire at the earlier of the code’s expiry and 24 hours in TEST or 30 days in LIVE. For other application or email links, see Track link engagement. Invalid or expired links do not redirect. A temporary verification failure asks the guest to retry. Link visits do not sign in a guest or change consent. Repeated visits count once per offer email, and security scanners may count as visits: a click is not proof of a human visit, booking, payment, or redemption.
This behavior applies to newly sent offer emails. Authentication and unsubscribe links retain their separate behavior. Already-sent provider tracking links are not rewritten retroactively.
The installation needs offers.read and offers.write, a TEST server key, the
exact registered collection-owned STAGING origin, and registered TEST recipients.
Publishable browser keys and LIVE credentials cannot call these operations.
Keep the server key in the server environment; the browser uses your same-origin
BFF. Never add a provider key to application code or use a private capture API.
Add the server and Next handlers
Section titled “Add the server and Next handlers”import { createKismetClient } from '@kismet-tech/sdk/server';import { createKismetGuestAuthNextHandlers } from '@kismet-tech/sdk/next';
const server = createKismetClient({ apiKey: process.env.KISMET_SERVER_KEY, collection: 'your-collection', baseUrl: 'https://api.ksmt.app/v1',});const offerId = process.env.KISMET_OFFER_ID!; // configured canonical perk UUIDconst handlers = createKismetGuestAuthNextHandlers({ ...yourExistingGuestHandlerConfig, offers: { client: server.offers, offerIds: [offerId] },});Map GET /api/kismet/guest/offers/[offerId] to handlers.offer(request, offerId)
and POST /api/kismet/guest/offers/[offerId]/captures to
handlers.captureOffer(request, offerId). The existing auth CSRF route remains
required. The handlers enforce configured offer IDs and exact site origins,
validate CSRF on POST, and derive origin from the authorized request URL.
Caller-supplied origin, environment, code and extra fields are rejected.
For direct server integrations, use server.offers.get(offerId, { origin })
and server.offers.capture(offerId, { ...input, origin }). Both operations send
the exact Origin header; capture also sends the matching body origin.
Display the disclosure before submission
Section titled “Display the disclosure before submission”import { createKismetGuestBrowserClient } from '@kismet-tech/sdk/react';const guest = createKismetGuestBrowserClient();const offer = await guest.offers.get(offerId);// Render offer.headline, offer.promise, offer.terms and offer.disclosure.text.// Only the explicit submit handler below may capture the offer.const result = await guest.offers.capture(offerId, { email, idempotencyKey: stableSubmissionKey, consent: { accepted: true, disclosureHash: offer.disclosure.hash },});Typing an email or signing in with Google does not submit the offer. The canonical TEST disclosure explains that this sends one real staging email without LIVE marketing enrollment or membership. Render that exact disclosure and send its hash only after the guest chooses the submit action. A changed disclosure must be displayed again before a fresh explicit submission.
Keep the same 16–200 character alphanumeric, hyphen or underscore idempotency key and identical payload for retries, including after a timeout. Do not generate a new key just because the response is uncertain. The API also deduplicates by recipient, offer and installation, so changing keys cannot produce another send.
Report the actual result
Section titled “Report the actual result”email.status: 'accepted' means the provider accepted the email. It does not prove
inbox delivery or redemption. not_sent, pending, unknown and failed are
separate outcomes; never present them as sent. Preserve the submission key when
checking again. The response includes replayed and the durable captureId.
The API records the canonical award event and capture/send state. Engagement clicks and Google login are not award, delivery, redemption or marketing-consent proof. Do not infer dashboard fulfillment counts from those events.
See read the offer and capture the offer for the REST contract.