Documentation / Collect consent in your app
BUILD

Collect consent in your app

Add an optional marketing checkbox wherever it fits your customer journey, then save the contact and their choice automatically from your backend.

1. Set up once, capture automatically

Use your own signup, checkout, booking, registration, or account form. Your backend calls POST /v1/contacts after the person submits their choice; operators do not need to enter each contact in the dashboard. Start with an approved workspace and a server-side API key with contacts:write. The key selects the workspace, so tenant_id is omitted from these examples.

2. Make the checkbox clear and optional

Keep the checkbox unchecked initially and separate from required terms. Name the business, describe the email content, and set an honest frequency expectation. Let the person complete their original task without subscribing. Adapt this example to your actual business and privacy notice; keep a versioned copy of the wording you show.

<form method="post" action="/signup">
  <!-- Your existing fields and CSRF protection belong here. -->
  <label for="marketing-opt-in">
    <input id="marketing-opt-in" type="checkbox"
      name="marketing_opt_in" value="yes">
    Email me occasional product updates and offers from Acme.
    I can unsubscribe anytime.
  </label>
  <a href="/privacy">How Acme uses your information</a>
  <button type="submit">Create account</button>
</form>

3. Send the choice from your backend

Run your normal form validation, CSRF checks, and abuse controls first. For an account preference, use the authenticated customer's email rather than accepting an arbitrary target address. Parse the checkbox explicitly: for this HTML form, only formData.get("marketing_opt_in") === "yes" means opt-in. For JSON, require the boolean true; Boolean("false") is true and must not be used to interpret consent.

// Node.js 24+, server-side only.
// NOTIFICATIONS_ORIGIN is your Notifications public API origin,
// for example https://notifications.bytefoundry.co.in.
// NOTIFICATIONS_API_KEY needs contacts:write in your workspace.
async function syncSignupContact({ email, name, marketingOptIn }) {
  const origin = process.env.NOTIFICATIONS_ORIGIN;
  const apiKey = process.env.NOTIFICATIONS_API_KEY;
  if (!origin || !apiKey) throw new Error("Configure Notifications on the server");

  const payload = { email, name };
  if (marketingOptIn === true) {
    payload.marketing_consent = true;
    // Server-owned identifier for the actual form and wording version.
    payload.consent_source = "signup-form:marketing-email:v1";
  }

  const response = await fetch(new URL("/v1/contacts", origin), {
    method: "POST",
    headers: {
      Authorization: "Bearer " + apiKey,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
    signal: AbortSignal.timeout(8000),
  });
  if (!response.ok) {
    throw new Error("Notifications contact sync failed: HTTP " + response.status);
  }
  return (await response.json()).data;
}

// Inside your server's form handler, after validating the customer:
// const contact = await syncSignupContact({
//   email: customer.email,
//   name: customer.name,
//   marketingOptIn: formData.get("marketing_opt_in") === "yes",
// });

4. Preserve consent when the box is unchecked

When this optional checkbox is unchecked, omit both marketing_consent and consent_source. POST /v1/contacts still saves or updates the contact: a new contact defaults to no marketing consent, and an existing contact retains its previous choice. Do not send false merely because the checkbox is absent from a signup or checkout form. Explicit false is a revocation, not an ordinary profile sync.

Field / scopeDescriptionRequirement / access
CheckedSend marketing_consent:true with consent_sourceExplicit opt-in
Unchecked / not shownOmit both consent fieldsPreserve existing choice
Explicit unsubscribeSend marketing_consent:falseRevoke and suppress marketing

5. Keep evidence with the choice

Notifications records the supplied consent source, API-key actor, consent event timestamp, and consented_at when permission is granted. Keep the corresponding form wording, version, customer action time, and capture context in your application. Construct consent_source on the server from that real capture point; a source label alone does not prove the person selected the checkbox. Do not keep sending true during later profile updates unless the person makes a fresh opt-in choice.

6. Choose single or double opt-in deliberately

This example uses single opt-in: your backend records the explicit checkbox choice without a confirmation email. The contacts API does not verify mailbox ownership or send a double opt-in email. Check which confirmation policy applies to your audience. If you require double opt-in, keep the request pending in your own application and omit marketing_consent until the recipient confirms; only then send true with the confirmation evidence.

7. Handle failures and retries without replaying old choices

Save the requested preference durably with the customer's normal form submission. If contact sync fails, show an accurate pending or failure state and use your own durable job or outbox for recovery; do not report that the preference was saved in Notifications before acceptance. Retry temporary network failures, HTTP 408, 429, and 5xx responses with bounded backoff, respecting Retry-After when supplied. Fix validation, authentication, scope, approval, or contact-limit errors before retrying.

8. Honor unsubscribe and suppression

Use built-in unsubscribe links in marketing messages. For an explicit unsubscribe in your own preferences screen, send false with the source of that action. This adds a marketing suppression. Recording true later does not remove it, so do not present the contact as eligible for marketing solely because the returned marketing_consent is true. Inspect suppressions with contacts:read or the dashboard; the current public API does not provide a suppression-removal or self-service resubscription flow.

9. Verify your integration before release

In a nonproduction workspace, check a new opt-in, an unchecked new contact, and an existing subscriber submitting an unchecked optional form. Verify that the source and actor are recorded, the unchecked form preserves earlier consent, and ordinary profile sync omits consent. Exercise a failed request and a retry superseded by withdrawal. Confirm that revoked or suppressed contacts remain ineligible for marketing and that no API key appears in browser requests or page source.

Building something interesting?We’d love to help