Campaign SDK

Creative changes with hard boundaries

The SDK only touches containers and fields that your page declares. Campaign content arrives as inert JSON, is validated against the current DOM, and never becomes executable markup.

Declare the baseline

Keep your normal fallback content in the document. Mark one unambiguous container with data-tday-slot, then mark each replaceable descendant with a unique data-tday-field.

HTML
<section data-tday-slot="homepage-hero">
  <p data-tday-field="eyebrow">Original eyebrow</p>
  <h1 data-tday-field="title">Original title</h1>
  <img data-tday-field="product" src="/original.webp" alt="Original product">
  <a data-tday-field="cta" href="/original">Original CTA</a>
</section>

Publish an inert manifest

A campaign arm can target the declared slot with version 1 JSON. The optional fingerprint binds the published creative to the expected template structure.

Campaign manifest
{
  "version": 1,
  "slots": [
    {
      "slot": "homepage-hero",
      "fingerprint": "dom-v1-0123456789abcdef",
      "fields": {
        "eyebrow": {
          "kind": "text",
          "value": "Built for busy teams"
        },
        "title": {
          "kind": "text",
          "value": "Launch your next campaign"
        },
        "product": {
          "kind": "image",
          "src": "https://cdn.example.com/product.webp",
          "alt": "The product on a desk"
        },
        "cta": {
          "kind": "link",
          "href": "https://example.com/signup",
          "text": "Start now",
          "target": "_self"
        }
      },
      "classes": ["campaign-active"],
      "cssVariables": { "--campaign-accent": "#ffb400" }
    }
  ]
}

Supported field kinds

KindWhat it can change
textText content, allowlisted attributes, classes, and CSS variables.
imageHTTP(S) source, required alt text, optional dimensions, and safe presentation fields.
videoHTTP(S) source, poster, label, and bounded playback flags.
linkHTTP(S) destination, optional text, and _self or _blank target.

Bind to a template fingerprint

Use fingerprintSlot() while preparing the manifest. At runtime, a mismatch leaves the baseline untouched instead of applying content to an unexpected structure.

TypeScript
import { fingerprintSlot } from "@designtday/campaign-sdk";

const slot = document.querySelector('[data-tday-slot="homepage-hero"]');
if (slot) {
  console.log(fingerprintSlot(slot));
}

Use a headless decision

Set autoApply: false when your renderer owns the UI. The assignment exposes its signed token, IDs, channel, sanitized manifest, optional published page model, and destination URL. Report exposure only after your experience is actually visible.

TypeScript
import { init } from "@designtday/campaign-sdk";

const campaign = init({
  siteKey: "td_pub_your_public_installation_key",
  campaign: "homepage-launch",
  autoApply: false,
  consent: "granted",
});

const decision = await campaign.ready;
const assignment = decision?.assignments[0];

if (assignment) {
  renderCampaignExperience(assignment.manifest, assignment.page);
  campaign.expose(assignment.token);
}

Automatic viewable exposure

With automatic application, an exposure is queued only after consent is granted and at least one applied slot remains at least 50 percent visible for one continuous second. Each assignment token is exposed at most once by that client instance. Browser exposure remains an unverified reporting event.

Continue reading