Skip to main content

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

1

Customer completes checkout

Creates order #123
2

Upsell page loads

Problem: How does it know the order ID?
3

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):
KeyDescriptionExample
orderCurrent/latest orderMain purchase
customerCustomer informationEmail, name, tags
paymentPayment detailsStatus, transaction ID
checkoutCheckout sessionToken, session data
subscriptionSubscription infoPlan, billing cycle
productProduct detailsSKU, 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

// Good
resources: {
  order: { ... },
  customer: { ... },
  payment: { ... }
}

// Bad
resources: {
  data1: { ... },
  info: { ... }
}
// Good
resources: {
  order: { id: 'ord_123', amount: 100 }
}

// Bad (no ID)
resources: {
  order: { amount: 100 }
}
// Good
resources: {
  mainOrder: { id: 'main' },
  upsellOrder: { id: 'upsell' }
}

// Bad (ambiguous)
resources: {
  order1: { ... },
  order2: { ... }
}
// 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);

Check Available Resources in DevTools

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):
ConditionDescriptionParameters
mainOrder.existsMain order was createdNone
mainOrder.hasProductOrder contains specific productproductId, variantId
mainOrder.totalGreaterThanOrder total exceeds amountamount (in cents)

Last Order Conditions

Check the most recent order (from previous step - could be upsell/downsell):
ConditionDescriptionParameters
lastOrder.existsPrevious step created an orderNone
lastOrder.hasProductOrder contains specific productproductId, variantId
lastOrder.totalGreaterThanOrder total exceeds amountamount (in cents)

Offer Conditions

Check offer acceptance from the previous step:
ConditionDescriptionParameters
offer.acceptedCustomer accepted the offerNone
offer.declinedCustomer declined the offerNone
offer.acceptedWithIdAccepted specific offerofferId

Payment Conditions

Check payment outcome from the previous step:
ConditionDescriptionParameters
payment.successPayment was successfulNone
payment.failedPayment failedNone
payment.amountGreaterThanPayment exceeds amountamount (in cents)

Customer Conditions

Check customer attributes (tags assigned via checkout or CRM):
ConditionDescriptionParameters
customer.hasTagCustomer has specific tagtag (e.g., “segment:vip”)
customer.hasTagPrefixHas any tag with prefixprefix (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