Campaign SDK
Send trusted outcomes from your backend
Use the conversions endpoint for completed business outcomes that should become server-verified campaign evidence. Every request needs the signed assignment and the confidential installation key.
Carry the assignment to your backend
A decision assignment includes an origin-bound, signed token. Send that token to your own backend alongside the action that may later convert. Keep your customer record and PII in your system. tday needs the attribution token, not the person's identity.
const decision = await campaign.ready;
const assignmentToken = decision?.assignments[0]?.token;
await fetch("/api/checkout", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
productId: "team-plan",
assignmentToken
})
});Post the completed conversion
When the outcome is durable, send one event to POST https://tday.com/api/sdk/v1/conversions. Use an ID derived from the durable operation so a worker retry sends the same ID again.
const conversion = {
id: `order_${order.publicId}`, // stable across retries
name: "purchase_completed", // exact campaign primary event
assignmentToken: order.assignmentToken,
occurredAt: order.completedAt.toISOString(),
valueMinor: order.totalMinor,
currency: "USD",
properties: {
plan: order.planKey,
billingCycle: order.billingCycle
}
};
const response = await fetch(
"https://tday.com/api/sdk/v1/conversions",
{
method: "POST",
headers: {
authorization: `Bearer ${process.env.TDAY_CAMPAIGN_SECRET}`,
"content-type": "application/json"
},
body: JSON.stringify(conversion)
}
);
if (!response.ok) {
throw new Error(`Campaign conversion failed: ${response.status}`);
}
const result = await response.json();
// { ok: true, accepted: 1, duplicates: 0 }curl https://tday.com/api/sdk/v1/conversions \
-H "Authorization: Bearer $TDAY_CAMPAIGN_SECRET" \
-H "Content-Type: application/json" \
-d '{
"id": "order_01J8M6Y7QK",
"name": "purchase_completed",
"assignmentToken": "td_at_signed_assignment_token",
"occurredAt": "2026-08-26T18:30:00.000Z",
"valueMinor": 12900,
"currency": "USD",
"properties": { "plan": "team" }
}'Make the event count
Match the primary event exactly
The event name must exactly equal the campaign's configured lowercase primary event to enter event-based optimizer evidence. Valid custom names are stored, but they do not count as the primary conversion.
Stay inside the attribution window
A primary conversion must follow the signed assignment and occur within the campaign attribution window. The timestamp cannot be over five minutes in the future or more than 90 days old.
Use the campaign currency
If valueMinor is present, include a three-letter currency code. It must match the campaign currency. The value is a non-negative integer in minor units.
Reuse the event ID on retry
IDs allow 1 to 128 safe letters, numbers, periods, underscores, tildes, or hyphens. Reusing one within the installation returns it as a duplicate instead of adding another conversion.
Send a batch
Batch one to 50 events with the conversions wrapper. The endpoint also accepts one event directly, a conversionwrapper, or an events array.
{
"conversions": [
{
"id": "order_01J8M6Y7QK",
"name": "purchase_completed",
"assignmentToken": "td_at_signed_assignment_token",
"occurredAt": "2026-08-26T18:30:00.000Z",
"valueMinor": 12900,
"currency": "USD"
}
]
}Trust and provider evidence
Accepted endpoint events are stored with server-verified trust. tday also reconciles aggregate conversions reported by connected paid providers as provider-verified evidence. Provider aggregates do not need a browser event-name match, and overlapping sources are combined conservatively rather than blindly added together.
Continue reading