Skip to main content

Funnel Lifecycle: Save, Publish & Go-Live

This guide covers the full lifecycle of a funnel managed through the API: duplicating an existing funnel, saving it (which mounts staging routes), previewing it, and publishing it to a production custom domain. It mirrors exactly what the CRM editor does when you click Save and Publish.
If you’re creating your first funnel from scratch, start with the Funnel Orchestrator guide. This page focuses on the staging → production lifecycle.

The two environments of a funnel

Every funnel has two configurations:
EnvironmentFieldServed onUpdated by
Staging (draft)config{funnel-slug}--{storeId}.cdn.tagada.ioPUT /funnels/{funnelId} (Save)
Production (live)productionConfigYour custom domain (e.g. pay.example.com)POST /funnels/{funnelId}/promote (Publish)
The staging CDN alias is derived from the funnel’s config.id (its slug) and the store id. Example: a funnel with slug my-funnel on store_abc123 is served at https://my-funnel--store_abc123.cdn.tagada.io.

The golden workflow: Save, then Publish

The CRM editor always performs these two steps in order — and so should you:
1

Save — PUT /funnels/{funnelId}

Persists the staging config, mounts the staging routes on the CDN alias, computes URL → step mappings (including path remaps), and triggers page prebuilds.
2

Publish — POST /funnels/{funnelId}/promote

Copies the staging routes to your custom domain, clones plugin instances for production isolation, and writes productionConfig.
Never promote a funnel that has not been saved at least once. Promote inherits its route matchers and path remaps from the mounted staging routes. If you create a funnel and immediately promote it, the staging alias will 404 and production routes lose their path remapping (e.g. a step with path: /checkout remapped to pagePath: /checkout-secure will only answer on /checkout-secure).

PUT replaces the entire config

PUT /funnels/{funnelId} is a full replace, not a patch. Sending a partial config (for example {"config": {"name": "New name"}}) will erase your funnel’s steps. Always follow the read-modify-write pattern:
# 1. Read the current config
curl https://app.tagadapay.com/api/public/v1/funnels/funnelv2_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY" > funnel.json

# 2. Modify the config object (jq, script, or by hand)

# 3. Write the COMPLETE config back
curl -X PUT https://app.tagadapay.com/api/public/v1/funnels/funnelv2_abc123 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "storeId": "store_abc123",
    "config": { /* the FULL config object, with ALL nodes and edges */ }
  }'
The response tells you what routing operations were performed:
{
  "funnel": { "id": "funnelv2_abc123", "name": "My Funnel" },
  "operations": [
    { "type": "mount", "stepId": "step_checkout", "status": "completed" },
    { "type": "mount", "stepId": "step_thankyou", "status": "completed" }
  ],
  "summary": { "total": 2, "mounted": 2, "unmounted": 0, "swapped": 0, "failed": 0 }
}

Duplicate an existing funnel

There is no dedicated duplicate endpoint — duplication is a read + create + save sequence:
1

Read the source funnel

GET /funnels/{sourceFunnelId} and take its config.
2

Sanitize the config

Before creating the copy:
  • Change config.id (the slug) — it determines the staging alias and must be unique per store
  • Change config.name
  • Remove every routeId from node configs (they belong to the source funnel’s routes)
  • Reset every mountPointId / url on node configs (they point to the source funnel’s domains)
3

Create the copy

POST /funnels with the sanitized config.
4

Save it

PUT /funnels/{newFunnelId} with the same config — this mounts the staging routes and prebuilds the pages.
Typical use case: cloning a live funnel to point it at a different payment flow (e.g. migrating to a new processor) — after step 3, update nodes[].config.stepConfig.payment.paymentFlowId on the checkout node before saving.

Preview the staging funnel

Create a preview session to test the staging environment without real payments:
curl -X POST https://app.tagadapay.com/api/public/v1/funnels/funnelv2_abc123/preview-session \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "storeId": "store_abc123", "funnelId": "funnelv2_abc123", "stepId": "step_checkout" }'
Then open the staging alias with the returned session:
https://my-funnel--store_abc123.cdn.tagada.io/checkout?funnelSessionId=fs_...&token=eyJ...&funnelEnv=staging

Publish to a production domain

curl -X POST https://app.tagadapay.com/api/public/v1/funnels/funnelv2_abc123/promote \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "storeId": "store_abc123",
    "customDomain": "pay.example.com",
    "acknowledgeConflict": true
  }'
FieldDescription
customDomainThe production domain the funnel goes live on. Must already be attached to your store.
acknowledgeConflictSet true to confirm replacing routes another funnel currently owns on that domain.
Response:
{
  "success": true,
  "message": "Funnel promoted to pay.example.com",
  "funnelId": "funnelv2_abc123",
  "productionUrl": "https://pay.example.com",
  "stagingUrl": "https://my-funnel--store_abc123.cdn.tagada.io",
  "routesCopied": 2,
  "instancesCloned": 2
}
Promote is idempotent: re-running it re-copies the current staging routes to production. To ship changes to a live funnel, Save (PUT) then Publish (promote) again.

Troubleshooting

Every funnel endpoint requires your API key as a Bearer token:
Authorization: Bearer YOUR_API_KEY
Watch out for an empty variable in shell scripts (Authorization: Bearer with nothing after it) — it produces the same 401 as a missing header. Verify with echo ${#KEY} before calling.
Older API versions returned this confusing 500 when the Authorization header was missing, empty, or not a recognized key format. It never indicated a server problem — only that the API key didn’t reach the server. Current versions return a clear 401 explaining how to pass the key. Fix: send Authorization: Bearer YOUR_API_KEY with a non-empty, valid key.
The funnel was created (and possibly promoted) but never saved, so no staging routes were mounted. Run PUT /funnels/{funnelId} with the full config once, then retry.
A step can declare a public path (e.g. /checkout) remapped to an internal pagePath (e.g. /checkout-secure). The remap is materialized when staging routes are mounted (Save). If you promoted before ever saving, re-run Save then Publish to restore the remap.

Doing this from an AI agent (MCP)

The CRM MCP server currently exposes funnels read-only (funnels.list, funnels.retrieve) — useful for diagnosing configuration. For mutations (create, save, promote), point your agent at the REST endpoints on this page with the same API key; they are designed to be safely scriptable.

Endpoint reference

EndpointMethodLifecycle role
/funnels/{id}GETRead config (start of every read-modify-write)
/funnelsPOSTCreate (or duplicate from a copied config)
/funnels/{funnelId}PUTSave — persist staging config + mount staging routes
/funnels/{funnelId}/promotePOSTPublish — copy staging routes to the production domain
/funnels/{funnelId}/preview-sessionPOSTTest staging without real payments