Rovenue SDK
Funnels

Publishing

What a funnel is checked for before it goes live, which findings block a publish and which only warn, and what visitors see while you keep editing.

Editing a funnel edits its draft. Visitors keep seeing the last published version until you publish again, so an unfinished screen is never live.

What publishing checks

The draft is validated as a whole. Findings come back in two kinds: blockers stop the publish, warnings do not. A blocker is a finding whose severity is error; a warning is severity warning. The tables below list every code the publish path can emit — the Rust judge plus the product gates the publish route adds on top — with the meaning from the message at the site that mints it. A script checks the tables against those sites, so a code nobody emits cannot linger here and a code the judge can emit cannot go undocumented.

Blockers

Both targets

The document and its screens

FindingWhat it meansSource
EMPTY_FLOWThe funnel has no screens at allpackages/funnel-core-rs/src/validate.rs:247
MALFORMED_CONFIGThe draft does not decode; the message is the decoder's own error, and nothing else is judged until it parsespackages/funnel-core-rs/src/validate.rs:292
DUPLICATE_PAGE_IDTwo screens share one idpackages/funnel-core-rs/src/validate.rs:305
ORDER_NAMES_MISSING_PAGEThe screen order names a screen that does not existpackages/funnel-core-rs/src/validate.rs:320
PAGE_MISSING_FROM_ORDERA screen is missing from the screen order, so only an explicit jump can ever reach itpackages/funnel-core-rs/src/validate.rs:333
DUPLICATE_BLOCK_IDTwo elements on one screen share one idpackages/funnel-core-rs/src/validate.rs:353
DUPLICATE_QUESTION_IDTwo inputs on different screens write the same answer keypackages/funnel-core-rs/src/validate.rs:537
QUESTION_WITHOUT_OPTIONSA choice input with no options — it would render unanswerablepackages/funnel-core-rs/src/validate.rs:370
BLOCK_TOO_DEEPStacks nested more than 3 deeppackages/funnel-core-rs/src/validate.rs:1069
UNKNOWN_PAGE_REFA jump or branch points at a screen that does not existpackages/funnel-core-rs/src/validate.rs:556

Questions, variables and text

FindingWhat it meansSource
UNKNOWN_QUESTION_REFA rule reads an answer key no screen asks forpackages/funnel-core-rs/src/validate.rs:586
CLAUSE_INVALID_OPA rule uses an operator nothing can compare, so it would publish and never matchpackages/funnel-core-rs/src/validate.rs:602
CLAUSE_NEVER_MATCHESA rule compares a Yes/No answer with anything else — it can never match; compare it with true or falsepackages/funnel-core-rs/src/validate.rs:614
UNKNOWN_TEXT_REFCopy interpolates a key no question, variable or app value supplies, so it renders as an empty stringpackages/funnel-core-rs/src/validate.rs:482
VARIABLE_INVALID_NAMEA variable whose name is not lower_snake_casepackages/funnel-core-rs/src/validate.rs:1014
VARIABLE_INVALID_VALUEA variable with an invalid valuepackages/funnel-core-rs/src/validate.rs:1052
VARIABLE_SHADOWS_QUESTIONA variable reusing an answer key — it would overwrite the answerpackages/funnel-core-rs/src/validate.rs:1025
VARIABLE_SHADOWS_EXTERNALA variable reusing an app value's namepackages/funnel-core-rs/src/validate.rs:767
VARIABLE_ANSWER_WITHOUT_QUESTION"Copy the answer" on a screen that asks nothingpackages/funnel-core-rs/src/validate.rs:1038
EXTERNAL_INVALID_NAMEAn app value with an invalid namepackages/funnel-core-rs/src/validate.rs:737
EXTERNAL_DUPLICATEAn app value declared more than oncepackages/funnel-core-rs/src/validate.rs:743
EXTERNAL_COLLIDES_WITH_QUESTIONAn app value sharing its name with an answer keypackages/funnel-core-rs/src/validate.rs:749
EXTERNAL_TOO_MANYMore app values declared than the 20 allowedpackages/funnel-core-rs/src/validate.rs:725

Actions, routing and timing

FindingWhat it meansSource
ACTION_EMPTY_CHAINA trigger with an empty action chain — it does nothingpackages/funnel-core-rs/src/validate.rs:810
ACTION_AFTER_TERMINALAn action below one that leaves the screen — it can never runpackages/funnel-core-rs/src/validate.rs:847
ACTION_SELF_TARGETA screen that navigates to itselfpackages/funnel-core-rs/src/validate.rs:862
ACTION_INVALID_URLAn open-link action with no URLpackages/funnel-core-rs/src/validate.rs:872
ACTION_INVALID_IDA custom action with an invalid action idpackages/funnel-core-rs/src/validate.rs:882
ELEMENT_UNKNOWN_TARGETA show/hide action naming an element that is not on the screenpackages/funnel-core-rs/src/validate.rs:899
CONDITIONAL_NESTEDA rule nested inside another rulepackages/funnel-core-rs/src/validate.rs:917
CONDITIONAL_EMPTYA rule with no condition to test, or nothing to runpackages/funnel-core-rs/src/validate.rs:926
ROUTING_NO_FALLBACKA branching chain with no Otherwise step for when no rule matchespackages/funnel-core-rs/src/validate.rs:691
WAIT_OUT_OF_BOUNDS"Advance automatically after" is set but is not a whole number of milliseconds inside 400–15000 (0.4–15s)packages/funnel-core-rs/src/validate.rs:401
AUTO_ADVANCE_WITH_INPUTA screen that advances on a timer but also holds something the timer would skip or pull the visitor away frompackages/funnel-core-rs/src/validate.rs:426

Selling and legacy rules

A web funnel sells through a checkout action on a button, which buys the package the visitor picked in a packageList on the same screen. See Checkout. A web funnel with nothing to sell — a quiz or a lead-gen funnel — still publishes, with a MISSING_CHECKOUT warning (see Warnings). A native funnel sells through the app store, so it needs no checkout — but the offering rules below apply to both targets: a native package list with no offering, or one of several, would draw nothing on the device.

FindingWhat it meansSource
PACKAGE_LIST_WITHOUT_OFFERINGA package list naming no offering; every list must name one, on web and nativepackages/shared/src/funnel/checkout.ts:188
MULTIPLE_OFFERINGSThe package lists name more than one offering; a funnel sells one, on web and nativepackages/shared/src/funnel/checkout.ts:202
OFFERING_NOT_FOUNDThe named offering does not exist in this project; the editor's drawer reports it as you edit, and publish checks it again against the catalogpackages/shared/src/funnel/checkout.ts:236
STALE_LEGACY_RULESA screen with a routing chain and legacy rules at once; the rules are invisible in the builder but would still firepackages/shared/src/funnel/document.ts:328
LEGACY_PAYWALL_RULE_UNRESOLVEDAn older branching rule going to "the paywall", but the funnel has no paywall screen — point the rule at a screenpackages/shared/src/funnel/document.ts:355

Paywall blocks, store values and project links

Store values ({{product.price}}, {{product.savings_percent}}, {{product.trial_days}}, …) and project links ({{project.terms_url}}, {{project.privacy_url}}) are filled at run time, beside the document — see Values.

FindingWhat it meansSource
KEY_HAS_DOTAn answer key, external or variable name contains a . — the dot separates a namespace from its value (product.price), so the name could never be referenced unambiguouslypackages/funnel-core-rs/src/validate.rs:150
PRODUCT_REF_WITHOUT_LIST{{product.…}} on a screen with no package list — there is no package for it to describepackages/funnel-core-rs/src/validate.rs:153
PRODUCT_REF_IN_LABEL{{product.…}} in copy that is always drawn (a button, option, table cell or feature title); only headings, texts, package badges/captions and feature bodies can hide when the store has no value — put price and trial disclosure in a text linepackages/funnel-core-rs/src/validate.rs:159
PROJECT_LINK_UNSETThe funnel links to the project's Terms or Privacy URL, but the project has not set it — add it under Project settings → Legal linkspackages/funnel-core-rs/src/validate.rs:168
DEFAULT_PACKAGE_EMPTYA package list's "selected by default" package is set to an empty namepackages/funnel-core-rs/src/validate.rs:170
TABLE_SHAPEA comparison table outside 1–3 value columns or 1–12 rows, or a row whose cells do not match the columnspackages/funnel-core-rs/src/validate.rs:174
HIGHLIGHT_OUT_OF_RANGEA comparison table highlights a column it does not havepackages/funnel-core-rs/src/validate.rs:176
FEATURE_LIST_EMPTYA feature list with no itemspackages/funnel-core-rs/src/validate.rs:178
FEATURE_LIST_TOO_LONGA feature list with more than 8 itemspackages/funnel-core-rs/src/validate.rs:180
FEATURE_ICON_INVALIDA feature's emoji icon that is not exactly one emojipackages/funnel-core-rs/src/validate.rs:182
APPEARANCE_COLOR_INVALIDA colour or badge tone that is neither #RRGGBB nor a theme role (accent, onAccent, text, muted, card, border, success, onSuccess)packages/funnel-core-rs/src/validate.rs:185
LAYOUT_NOT_SUPPORTEDA grid layout or column count on a question kind that has no grid (only picture choice does), or a value outside list/grid and 2–3 columnspackages/funnel-core-rs/src/validate.rs:188

Web only

The checkout gates are web rules: their whole argument is a paid page that sells through Stripe, and the sellers they ask for — a checkout action or an expressCheckout block — are web-only capabilities the crate refuses on native. Likewise the publish route demands a Stripe account that can take a card only of a web funnel with a checkout, never of a native funnel whose purchase goes through the store.

A paywall screen does not count as a checkout. It's a display screen: it shows an offer and never takes a payment.

FindingWhat it meansSource
CHECKOUT_WITHOUT_PACKAGE_LISTA screen runs a checkout but has no package list of its own; the payment reads the offering from the checkout's own screenpackages/shared/src/funnel/checkout.ts:206
EXPRESS_WITHOUT_PACKAGE_LISTA screen carries an express checkout block but has no package list of its own; the block buys through the same endpoint, so the same rule appliespackages/shared/src/funnel/checkout.ts:215
NO_WAY_FORWARDA screen that is not the last one has nothing that could ever move the visitor on: no button that goes forward, jumps, closes the funnel or checks out, no answer option that does, and no "Advance automatically after" wait. Add a button, or end the screen's routing in Close funnel to make it a final screen. Native funnels are exempt: a page with no control at all is their documented Continue fallbackpackages/funnel-core-rs/src/validate.rs:448
STRIPE_NOT_CONNECTEDThe funnel has a checkout, but this project cannot take a card yet: finish Stripe onboardingapps/api/src/routes/dashboard/funnels.ts:835

Depends on the target

Whether a block or action may be carried at all depends on the funnel's target. These two findings name the capability the target lacks and say why in the message; the capability table lists what each target may carry.

FindingWhat it meansSource
BLOCK_NOT_ON_TARGETAn element the funnel's target cannot render — see the capability table for what each target allowspackages/funnel-core-rs/src/validate.rs:652
ACTION_NOT_ON_TARGETAn action the funnel's target cannot run — see the capability table for what each target allowspackages/funnel-core-rs/src/validate.rs:666

Warnings

FindingWhat it meansSource
UNREACHABLE_PAGENothing navigates to this screen — usually a leftover, sometimes deliberatepackages/funnel-core-rs/src/validate.rs:1206
PAGE_AFTER_PAYMENTFollows a payment, so it is never shown — a payment ends the funnelpackages/funnel-core-rs/src/validate.rs:1196
MISSING_CHECKOUTA web funnel where no button runs a checkout action and no screen carries an express checkout block, so nothing takes a payment. A quiz or lead-gen funnel publishes as it is; to take payment, add a button whose action is Checkoutpackages/shared/src/funnel/checkout.ts:190
OFFER_REF_UNGUARDEDTrial or intro-offer copy ({{product.trial_days}}, {{product.offer_price}}, …) in an element whose visibility never reads product.has_trial. It already hides for a visitor without the offer; pair it with a has_trial = false sibling so that visitor reads somethingpackages/funnel-core-rs/src/validate.rs:164
MEDIA_WITHOUT_SOURCEAn image, video or gallery with no source, so it draws nothing — normal while an asset is still being uploaded, a gap if it shipspackages/funnel-core-rs/src/validate.rs:1262

A screen after a payment is never shown: on the web a paid visitor sees the runner's success screen with the app hand-off; in the app your purchase handler takes over.

Why an unknown reference is a blocker

{{frist_name}} renders as an empty string, not as an error. "Hi " ships and nobody notices until a visitor reads it, which is why a reference nothing declares stops the publish rather than warning.

Versions

Publishing snapshots the draft: screens, theme, settings and the resolved routing. A session that is already running stays on the version it started with, so a visitor mid-funnel never jumps into a different funnel underneath them.

A published funnel is served at its slug — see Hosting.

On this page