Attribution is how Future Demand connects observed sales to the campaigns or audience packages that drove them. This guide shows where to read those results in Wave and how to configure attribution models. For the conceptual model, see Attribution concept.

Two surfaces

SurfaceUsed forEndpoints
Campaign attributionPer-event sales adjustments tied to specific campaigns. Lives on the Wave Campaign detail page./campaign_results/
Backhaul attributionPackage-level evaluation: which clusters converted, at what rate. Lives on the Package Builder Results page./package_builder/packages/{id}/backhaul_evaluation*

1. Campaign attribution

Read

GET /campaign_results/?eid=evt_abc123&partner_id=partner-id-acme
[
  {
    "id":         "cr_xyz",
    "eid":        "evt_abc123",
    "partner_id": "partner-id-acme",
    "purchase":   147,
    "revenue":    6615,
    "created_at": "2026-06-13T08:00:00Z",
    "source":     "automated"
  }
]

Render

The reference webapp surfaces these on the Event/Campaign detail page in two places:
  • As the manual_adjustments overlay on the daily revenue chart (the DailyRevenueChart reads them from /daily_revenue_summary/’s manual_adjustments array, which includes the same rows).
  • As individual rows in the “Attribution / adjustments” expandable panel.

Mutate (rarely)

POST /campaign_results/?eid=evt_abc123&partner_id=...&purchase=147&revenue=6615
DELETE /campaign_results/?id=cr_xyz
The platform runs the underlying attribution job on its own schedule — partners should not POST campaign_results directly except to record a manual adjustment that the user explicitly enters (e.g. “we sold 12 tickets at the box office that the data feed missed”).

2. Backhaul attribution (per package)

Configure

Each package can override the partner-level default attribution model.
# Read current per-package settings
GET /package_builder/packages/{packageId}/backhaul_evaluation_details

# Save (triggers re-eval)
PUT /package_builder/packages/{packageId}/backhaul_evaluation_details
{
  "backhaul_evaluation_attribution_window": 14,
  "backhaul_evaluation_model":              "EVENTS_FROM_PACKAGE",
  "email_sent_date":                        "2026-06-01"
}
Models supported:
ValueMeaning
ALL_PURCHASESCount any purchase by the customer in the window.
EVENTS_FROM_PACKAGEOnly count purchases of events in this package. Default.
EVENT_SERIESCount purchases in the same event series.
backhaul_evaluation_attribution_window is an integer (days) starting from email_sent_date.

Read results

GET /package_builder/packages/{packageId}/backhaul_evaluation
{
  "clusters": {
    "family_outings_munich": {
      "customers": 184,
      "items":     412,
      "purchase":  18540
    },
    "german_metal_diehards": {
      "customers": 132,
      "items":     287,
      "purchase":  12915
    }
  }
}
Aggregate customers / items / purchase across clusters client-side for header KPIs.

Partner-level defaults

GET  /partners/{partnerId}/backhaul_attribution_details
POST /partners/{partnerId}/backhaul_attribution_details
Same shape as the per-package endpoint, minus email_sent_date. Set once in your settings UI; each new package starts with these as defaults.

3. Lookout attribution model (per event)

Independent of Backhaul, each event has its own attribution-model selection that controls how the daily-revenue chart attributes sales:
GET /events/{eid}/attribution_model
PUT /events/{eid}/attribution_model
{ "model": "..." }
This is a per-event override on top of the partner-level default.

Reference implementation

export function PackageAttributionConfig({ packageId }: { packageId: string }) {
  const settings = useFd<BackhaulSettings>(
    `/package_builder/packages/${packageId}/backhaul_evaluation_details`,
  );

  const save = useCallback(async (next: BackhaulSettings) => {
    await fd.put(
      `/package_builder/packages/${packageId}/backhaul_evaluation_details`,
      next,
    );
    settings.refetch();
    invalidateQuery(`/package_builder/packages/${packageId}/backhaul_evaluation`);
  }, [packageId]);

  if (!settings.data) return <Skeleton />;
  return <SettingsForm initial={settings.data} onSave={save} />;
}

Gotchas

There’s no separate “run evaluation” button. The PUT kicks off the job; the GET on backhaul_evaluation reflects the result once it completes. The reference webapp uses an evaluationReloadKey counter to invalidate — replace with your query cache’s invalidation.
Changing the partner default only affects new packages. Existing packages keep whatever settings were stored at their last PUT.
Omit it for partner-level defaults. For per-package, set it to the day your partner sent the marketing communication; the window starts there.
Read /campaign_results/ for the output. The platform runs the underlying attribution job on its own schedule; there are no partner-callable endpoints to trigger it.