Funnel Resources: Simple Data Flow Between Steps
The Resource System lets you pass data (like orders, customers, payments) between funnel steps automatically. Think of it as a context bag that travels with your customer through the funnel.
Simple Concept : When a step creates data (like an order), it puts it in the resources basket. The next step can grab it from there. That’s it!
The Problem We Solve
Customer completes checkout
Creates order #123
Upsell page loads
Problem : How does it know the order ID?
Thank you page loads
Problem : How does it access order #123 AND upsell #456?
Solution : Resources! Each step puts its data in resources, and the next step reads it.
Basic Usage
Step 1: Import the Hook
import { useFunnel } from '@tagadapay/plugin-sdk/v2' ;
Step 2: Get the Funnel Context
export default function MyPlugin () {
const funnel = useFunnel ();
// Access data from previous steps
const order = funnel . context ?. resources ?. order ;
const customer = funnel . context ?. resources ?. customer ;
return (
< div >
< h1 > Order { order ?. id } </ h1 >
< p > Customer: { customer ?. email } </ p >
</ div >
);
}
Step 3: Pass Data to Next Step
export default function CheckoutPlugin () {
const funnel = useFunnel ();
const handlePaymentSuccess = async ( paymentResult ) => {
// Put data in resources for next step
await funnel . next ({
type: 'payment_success' ,
data: {
resources: {
order: {
id: paymentResult . orderId ,
amount: 99.99 ,
currency: 'USD'
},
customer: {
id: paymentResult . customerId ,
email: '[email protected] '
},
payment: {
id: paymentResult . paymentId ,
status: 'success'
}
}
}
});
};
return < PaymentForm onSuccess = { handlePaymentSuccess } /> ;
}
That’s it! The next step automatically receives these resources.
Real Example: Complete Checkout Flow
1. Checkout Page (Creates Order)
import { useFunnel , usePayment } from '@tagadapay/plugin-sdk/v2' ;
export default function CheckoutPage () {
const funnel = useFunnel ();
const { processPayment } = usePayment ();
const handleSubmit = async ( formData ) => {
const result = await processPayment ({
amount: 99.99 ,
currency: 'USD' ,
cardDetails: formData
});
if ( result . success ) {
// ✅ Pass order data to next step
await funnel . next ({
type: 'payment_success' ,
data: {
resources: {
order: {
id: result . order . id ,
amount: result . order . amount ,
currency: result . order . currency
},
customer: {
id: result . customer . id ,
email: formData . email
}
}
}
});
}
};
return < CheckoutForm onSubmit = { handleSubmit } /> ;
}
2. Thank You Page (Reads Order)
import { useFunnel } from '@tagadapay/plugin-sdk/v2' ;
export default function ThankYouPage () {
const funnel = useFunnel ();
// ✅ Read order from previous step
const order = funnel . context ?. resources ?. order ;
const customer = funnel . context ?. resources ?. customer ;
if ( ! order ) {
return < div > Loading... </ div > ;
}
return (
< div >
< h1 > Thank You! 🎉 </ h1 >
< p > Order # { order . id } confirmed </ p >
< p > Amount: $ { order . amount } { order . currency } </ p >
< p > Confirmation sent to { customer . email } </ p >
</ div >
);
}
Advanced: Multiple Orders (Upsells)
When you have multiple orders (main + upsells), use named resources :
Checkout: Main Order
await funnel . next ({
type: 'payment_success' ,
data: {
resources: {
// Generic (for next immediate step)
order: { id: 'ord_main' , amount: 99.99 },
// Named (can be referenced later)
mainOrder: { id: 'ord_main' , amount: 99.99 }
}
}
});
Upsell: Add Another Order
await funnel . next ({
type: 'offer_accepted' ,
data: {
resources: {
// New generic order (overwrites previous)
order: { id: 'ord_upsell' , amount: 49.99 },
// Specific name (preserved)
upsellOrder: { id: 'ord_upsell' , amount: 49.99 }
}
}
});
Final Page: Access Both Orders
const funnel = useFunnel ();
const mainOrder = funnel . context ?. resources ?. mainOrder ; // Still there!
const upsellOrder = funnel . context ?. resources ?. upsellOrder ; // Also there!
const totalRevenue = ( mainOrder ?. amount || 0 ) + ( upsellOrder ?. amount || 0 );
// Total: $149.98
Rule of Thumb :
Use order for “what just happened”
Use mainOrder, upsellOrder for “specific references”
Standard Resource Keys
These are the recommended resource keys (but you can use any key you want):
Key Description Example orderCurrent/latest order Main purchase customerCustomer information Email, name, tags paymentPayment details Status, transaction ID checkoutCheckout session Token, session data subscriptionSubscription info Plan, billing cycle productProduct details SKU, price, variants
Named References (for multiple instances):
mainOrder, upsellOrder, downsellOrder
mainProduct, addonProduct
Any custom name you want!
URL Parameters: Automatic Resolution
The orchestrator automatically fills in URL parameters from resources:
In Your Manifest
{
"path" : "/thankyou/:orderId"
}
Orchestrator Magic ✨
// You emit:
resources : { order : { id : 'ord_123' } }
// URL becomes:
/ thankyou / ord_123
// :orderId automatically resolved from order.id!
Query Parameters
{
"path" : "/checkout?token=:checkout.token"
}
// You emit:
resources : { checkout : { id : 'chk_123' , token : 'tok_abc' } }
// URL becomes:
/ checkout ? token = tok_abc
Common Patterns
Pattern 1: Simple Flow (One Order)
// Checkout
await funnel . next ({
data: {
resources: {
order: { id: '123' , amount: 100 }
}
}
});
// Thank You
const order = funnel . context ?. resources ?. order ;
Pattern 2: With Customer Data
// Checkout
await funnel . next ({
data: {
resources: {
order: { id: '123' , amount: 100 },
customer: { email: '[email protected] ' , name: 'John Doe' },
payment: { status: 'success' , method: 'credit_card' }
}
}
});
// Next step has access to all
Pattern 3: Upsell Flow
// Step 1: Main checkout
resources : {
order : { id : 'main' },
mainOrder : { id : 'main' }
}
// Step 2: Upsell
resources : {
order : { id : 'upsell' }, // New "latest"
upsellOrder : { id : 'upsell' } // Named reference
}
// Step 3: Thank you - has both!
const main = resources . mainOrder ;
const upsell = resources . upsellOrder ;
TypeScript Support
For full type safety, define your resource types:
import { FunnelResourceData } from '@tagadapay/plugin-sdk/v2' ;
// Define your types (must extend FunnelResourceData to have id field)
interface Order extends FunnelResourceData {
amount : number ;
currency : string ;
items : LineItem [];
}
interface Customer extends FunnelResourceData {
email : string ;
name : string ;
}
// Define custom resources
interface MyCustomResources {
order ?: Order ;
customer ?: Customer ;
}
// Use with funnel
const funnel = useFunnel < MyCustomResources >();
// Now you get IntelliSense!
const orderId = funnel . context ?. resources ?. order ?. id ; // ✅ Type-safe!
const email = funnel . context ?. resources ?. customer ?. email ; // ✅ Type-safe!
Best Practices
✅ DO: Use descriptive resource keys
// Good
resources : {
order : { ... },
customer : { ... },
payment : { ... }
}
// Bad
resources : {
data1 : { ... },
info : { ... }
}
✅ DO: Include IDs in all resources
// Good
resources : {
order : { id : 'ord_123' , amount : 100 }
}
// Bad (no ID)
resources : {
order : { amount : 100 }
}
✅ DO: Use named resources for multiple instances
// Good
resources : {
mainOrder : { id : 'main' },
upsellOrder : { id : 'upsell' }
}
// Bad (ambiguous)
resources : {
order1 : { ... },
order2 : { ... }
}
❌ DON'T: Store large binary data
// Bad
resources : {
order : {
id : '123' ,
imageData : '...10MB base64 string...' // ❌ Too large!
}
}
// Good
resources : {
order : {
id : '123' ,
imageUrl : 'https://cdn.example.com/image.jpg' // ✅ URL instead!
}
}
Debugging
Check Current Resources
const funnel = useFunnel ();
console . log ( 'All resources:' , funnel . context ?. resources );
console . log ( 'Order:' , funnel . context ?. resources ?. order );
console . log ( 'Customer:' , funnel . context ?. resources ?. customer );
Open browser console and run:
window . __TAGADA_FUNNEL_CONTEXT__ ?. resources
Funnel Step Conditions
Configure step routing conditions in the CRM Funnel Editor . The orchestrator evaluates these conditions to determine the next step.
Main Order Conditions
Check the main checkout order (first order created in the funnel):
Condition Description Parameters mainOrder.existsMain order was created None mainOrder.hasProductOrder contains specific product productId, variantIdmainOrder.totalGreaterThanOrder total exceeds amount amount (in cents)
Last Order Conditions
Check the most recent order (from previous step - could be upsell/downsell):
Condition Description Parameters lastOrder.existsPrevious step created an order None lastOrder.hasProductOrder contains specific product productId, variantIdlastOrder.totalGreaterThanOrder total exceeds amount amount (in cents)
Offer Conditions
Check offer acceptance from the previous step:
Condition Description Parameters offer.acceptedCustomer accepted the offer None offer.declinedCustomer declined the offer None offer.acceptedWithIdAccepted specific offer offerId
Payment Conditions
Check payment outcome from the previous step:
Condition Description Parameters payment.successPayment was successful None payment.failedPayment failed None payment.amountGreaterThanPayment exceeds amount amount (in cents)
Customer Conditions
Check customer attributes (tags assigned via checkout or CRM):
Condition Description Parameters customer.hasTagCustomer has specific tag tag (e.g., “segment:vip”)customer.hasTagPrefixHas any tag with prefix prefix (e.g., “utm_source”)customer.hasAnyTagHas ANY of selected tags (OR) tags (comma-separated)customer.hasAllTagsHas ALL of selected tags (AND) tags (comma-separated)
Advanced: JSONLogic
For complex conditions, use Advanced Mode in the Step Editor:
// AND: Both conditions must be true
{
"and" : [
{ "offer.accepted" : true },
{ "mainOrder.totalGreaterThan" : { "amount" : 10000 } }
]
}
// OR: Either condition can be true
{
"or" : [
{ "customer.hasTag" : { "tag" : "vip" } },
{ "lastOrder.totalGreaterThan" : { "amount" : 20000 } }
]
}
// NOT: Negate a condition
{
"!" : { "offer.declined" : true }
}
// Complex: Nested AND/OR
{
"and" : [
{ "payment.success" : true },
{
"or" : [
{ "customer.hasTag" : { "tag" : "segment:premium" } },
{ "mainOrder.totalGreaterThan" : { "amount" : 5000 } }
]
}
]
}
Tip : The Advanced Mode editor in the CRM provides syntax validation and a quick reference guide!
Migration from Legacy
If you’re upgrading from the old system:
Before (Legacy)
await funnel . next ({
data: {
orderId: '123' ,
orderAmount: 100 ,
customerId: '456'
}
});
After (Resource System v2)
await funnel . next ({
data: {
resources: {
order: { id: '123' , amount: 100 },
customer: { id: '456' }
}
}
});
Good News : The orchestrator auto-migrates legacy data, so old plugins still work!
Next Steps