Campaign Funnel Workflow
Use this guide to create campaigns, associate links, track conversions through the funnel, and measure campaign performance.
For a platform-wide, runnable lab that combines schema setup, analytics, and function-binding flows, use Platform-Wide Feature Exploration Lab.
When to use
- You want to track clicks-to-conversions for a marketing initiative.
- You need funnel metrics across multiple links in a campaign.
- You want to test conversions before going live.
- You need campaign-level analytics broken down by segment, country, or device.
What is a campaign?
A campaign is a container that groups links and conversion events under a shared goal. Campaigns enable:
- Goal-based tracking (product sale, lead capture, newsletter signup, affiliate clickout, app install).
- Funnel metrics: clicks → qualified clicks → conversions → revenue.
- Cross-link attribution when multiple links feed the same campaign.
- Campaign-scoped personalization rules.
Campaign lifecycle
DRAFT → ACTIVE → PAUSED
↑ ↓
└────────────────┘
| Status | Meaning |
|---|---|
DRAFT | Campaign created but not yet tracking. Default state. |
ACTIVE | Campaign is live and tracking clicks and conversions. |
PAUSED | Campaign is paused. Existing data preserved, no new tracking. |
Goal types
Each campaign has a goal type that sets default tracking behavior:
| Goal type | Default tracking preset | Default event type |
|---|---|---|
PRODUCT_SALE | ga4_purchase | purchase |
LEAD_CAPTURE | ga4_lead | lead |
NEWSLETTER | ga4_sign_up | newsletter_signup |
AFFILIATE_CLICKOUT | custom_webhook | affiliate_click |
APP_INSTALL | ga4_app_install | app_install |
Defaults can be overridden at campaign creation time.
End-to-end workflow
1. Create a campaign
POST /v2/handles/{handle}/campaigns
{
"campaignId": "spring-launch",
"goalType": "PRODUCT_SALE",
"status": "ACTIVE",
"primarySlug": "spring-promo"
}
The campaignId must match ^[a-z0-9][a-z0-9_-]{2,63}$. If omitted, one is auto-generated as cmp_{hex}.
The response includes a quickStart array with activation steps:
{
"campaign": { ... },
"quickStart": [
"Share the short URL in your campaign channel.",
"Click once to validate click/context tracking.",
"Post one conversion event to complete activation."
]
}
2. Associate links
Set the campaignId field when creating or updating a link:
PUT /v2/handles/{handle}/links/{slug}
{
"destinationUrl": "https://shop.example.com/spring",
"campaignId": "spring-launch",
"trackingPolicy": {
"mode": "DIRECT",
"appendContextParam": true
}
}
Multiple links can reference the same campaign. This is useful for A/B testing different creatives or channels.
2.5 Configure campaign detail structure page
Use campaign site-structure routes when each campaign needs its own detail page schema and login-gated content.
JWT control-plane routes:
PUT /v2/handles/{handle}/site-structure/campaigns/{campaignId}(upsert page + schema JSON)GET /v2/handles/{handle}/site-structure/campaigns/{campaignId}(fetch page + schema JSON)POST /v2/handles/{handle}/site-structure/campaigns/{campaignId}/publish(publish withFREE_WITH_LOGIN)
PAT automation routes:
PUT /v2/public/handles/{handle}/site-structure/campaigns/{campaignId}GET /v2/public/handles/{handle}/site-structure/campaigns/{campaignId}POST /v2/public/handles/{handle}/site-structure/campaigns/{campaignId}/publish
MCP tool equivalents:
campaign_structure_getcampaign_structure_upsertcampaign_structure_publish
Schema is stored under deterministic S3 keys: site-structure/{handle}/campaigns/{campaignId}/v{version}.json
Campaign records store a detailsPageSlug backreference for O(1) lookup.
Hands-on track with step-by-step commands:
3. Verify campaign health
GET /v2/handles/{handle}/campaigns/{campaignId}/health
Returns readiness checks:
{
"campaignId": "spring-launch",
"checks": [
{ "name": "tracking_preset", "status": "PASS", "detail": "Using tracking preset 'ga4_purchase'." },
{ "name": "conversion_event_type", "status": "PASS", "detail": "Using event type 'purchase'." },
{ "name": "primary_link", "status": "PASS", "detail": "Primary link configured: spring-promo" }
],
"readiness": "READY"
}
4. Test a conversion
POST /v2/handles/{handle}/campaigns/{campaignId}/test-conversion
{
"eventType": "purchase",
"value": 99.99,
"currency": "USD",
"metadata": {
"orderId": "12345",
"items": 3
}
}
Test conversions are marked with test: true in metadata and emit a conversion.tested webhook event.
5. Go live
Share the campaign's primary link. As visitors click and convert:
- Click events are recorded and attributed to the campaign.
- Campaign rollups are updated in real-time (clicks, bot clicks, conversions, revenue).
- Funnel metrics become available in the analytics API.
6. Monitor the funnel
GET /v2/handles/{handle}/analytics?funnel=true&campaignId=spring-launch
Returns the campaign funnel:
{
"funnel": [
{
"campaignId": "spring-launch",
"clicks": 1250,
"qualifiedClicks": 1200,
"conversions": 47,
"revenue": 5234.50,
"confidence": 87,
"breakdown": {
"source": [
{ "source": "email", "count": 800 },
{ "source": "social", "count": 400 }
],
"country": [
{ "country": "US", "count": 900 },
{ "country": "GB", "count": 300 }
],
"device": [
{ "device": "mobile", "count": 700 },
{ "device": "desktop", "count": 500 }
]
},
"lastSeenAt": "2026-02-25T12:34:56.000Z"
}
]
}
When campaignId is omitted, the response includes an aggregate funnel across all campaigns plus individual funnels for up to 50 campaigns.
Campaign-level segment analytics
Query segment breakdowns scoped to a campaign:
GET /v2/handles/{handle}/analytics?groupBy=segment&campaignId=spring-launch
Returns clicks, conversions, and revenue per visitor segment for the specified campaign.
Personalization rules with campaigns
Personalization rules can target specific campaigns using the campaignIn condition:
{
"ruleId": "spring-mobile-variant",
"enabled": true,
"priority": 100,
"when": {
"campaignIn": ["spring-launch"],
"deviceIn": ["MOBILE"]
},
"variant": {
"id": "mobile-optimized",
"destinationUrl": "https://m.shop.example.com/spring"
}
}
When a click arrives with a matching campaignId, the personalization engine evaluates the campaign condition alongside device, country, and source conditions.
UI path
- Open
https://app.{PUBLIC_DOMAIN}. - Navigate to your handle and open Campaigns.
- Create a new campaign with a goal type.
- Associate links to the campaign.
- Run the health check to verify readiness.
- Activate the campaign and share links.
- Monitor funnel metrics under Analytics.
Required auth
- CREATOR-level
JWTor above to create and manage campaigns. - CREATOR-level
JWTor above to query campaign analytics. PATwithanalytics.read(or wildcardanalytics.*) scope for automated funnel queries.PATwithpages.read/pages.writefor campaign site-structure automation.
API fallback
POST /v2/handles/{handle}/campaigns— Create campaignGET /v2/handles/{handle}/campaigns— List campaignsPUT /v2/handles/{handle}/site-structure/campaigns/{campaignId}— Upsert campaign detail structureGET /v2/handles/{handle}/site-structure/campaigns/{campaignId}— Fetch campaign detail structurePOST /v2/handles/{handle}/site-structure/campaigns/{campaignId}/publish— Publish campaign detail structure pageGET /v2/handles/{handle}/campaigns/{campaignId}/health— Campaign health checkPOST /v2/handles/{handle}/campaigns/{campaignId}/test-conversion— Test conversionGET /v2/handles/{handle}/analytics?funnel=true&campaignId={id}— Funnel metricsGET /v2/handles/{handle}/analytics?groupBy=segment&campaignId={id}— Segment breakdown
Common errors
| Code | Error | Cause |
|---|---|---|
| 400 | invalid_campaign_id | Campaign ID does not match required pattern |
| 404 | campaign_not_found | Campaign does not exist |
| 409 | campaign_conflict | Campaign ID already exists |
Related: