> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tagada.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Funnel Resources System

> Pass data between funnel steps with the Resource System v2.0

# 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.

<Info>
  **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!
</Info>

***

## The Problem We Solve

<Steps>
  <Step title="Customer completes checkout">
    Creates order #123
  </Step>

  <Step title="Upsell page loads">
    **Problem**: How does it know the order ID?
  </Step>

  <Step title="Thank you page loads">
    **Problem**: How does it access order #123 AND upsell #456?
  </Step>
</Steps>

**Solution**: Resources! Each step puts its data in `resources`, and the next step reads it.

***

## Basic Usage

### Step 1: Import the Hook

```tsx theme={null}
import { useFunnel } from '@tagadapay/plugin-sdk/v2';
```

### Step 2: Get the Funnel Context

```tsx theme={null}
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

```tsx theme={null}
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: 'user@example.com'
          },
          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)

```tsx theme={null}
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)

```tsx theme={null}
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

```tsx theme={null}
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

```tsx theme={null}
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

```tsx theme={null}
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
```

<Tip>
  **Rule of Thumb**:

  * Use `order` for "what just happened"
  * Use `mainOrder`, `upsellOrder` for "specific references"
</Tip>

***

## Standard Resource Keys

These are the recommended resource keys (but you can use any key you want):

| Key            | Description          | Example                |
| -------------- | -------------------- | ---------------------- |
| `order`        | Current/latest order | Main purchase          |
| `customer`     | Customer information | Email, name, tags      |
| `payment`      | Payment details      | Status, transaction ID |
| `checkout`     | Checkout session     | Token, session data    |
| `subscription` | Subscription info    | Plan, billing cycle    |
| `product`      | Product 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

```json theme={null}
{
  "path": "/thankyou/:orderId"
}
```

### Orchestrator Magic ✨

```typescript theme={null}
// You emit:
resources: { order: { id: 'ord_123' } }

// URL becomes:
/thankyou/ord_123

// :orderId automatically resolved from order.id!
```

### Query Parameters

```json theme={null}
{
  "path": "/checkout?token=:checkout.token"
}
```

```typescript theme={null}
// You emit:
resources: { checkout: { id: 'chk_123', token: 'tok_abc' } }

// URL becomes:
/checkout?token=tok_abc
```

***

## Common Patterns

### Pattern 1: Simple Flow (One Order)

```tsx theme={null}
// Checkout
await funnel.next({
  data: {
    resources: {
      order: { id: '123', amount: 100 }
    }
  }
});

// Thank You
const order = funnel.context?.resources?.order;
```

### Pattern 2: With Customer Data

```tsx theme={null}
// Checkout
await funnel.next({
  data: {
    resources: {
      order: { id: '123', amount: 100 },
      customer: { email: 'user@example.com', name: 'John Doe' },
      payment: { status: 'success', method: 'credit_card' }
    }
  }
});

// Next step has access to all
```

### Pattern 3: Upsell Flow

```tsx theme={null}
// 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:

```tsx theme={null}
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

<AccordionGroup>
  <Accordion title="✅ DO: Use descriptive resource keys">
    ```tsx theme={null}
    // Good
    resources: {
      order: { ... },
      customer: { ... },
      payment: { ... }
    }

    // Bad
    resources: {
      data1: { ... },
      info: { ... }
    }
    ```
  </Accordion>

  <Accordion title="✅ DO: Include IDs in all resources">
    ```tsx theme={null}
    // Good
    resources: {
      order: { id: 'ord_123', amount: 100 }
    }

    // Bad (no ID)
    resources: {
      order: { amount: 100 }
    }
    ```
  </Accordion>

  <Accordion title="✅ DO: Use named resources for multiple instances">
    ```tsx theme={null}
    // Good
    resources: {
      mainOrder: { id: 'main' },
      upsellOrder: { id: 'upsell' }
    }

    // Bad (ambiguous)
    resources: {
      order1: { ... },
      order2: { ... }
    }
    ```
  </Accordion>

  <Accordion title="❌ DON'T: Store large binary data">
    ```tsx theme={null}
    // 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!
      }
    }
    ```
  </Accordion>
</AccordionGroup>

***

## Debugging

### Check Current Resources

```tsx theme={null}
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:

```javascript theme={null}
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.exists`           | Main order was created          | None                     |
| `mainOrder.hasProduct`       | Order contains specific product | `productId`, `variantId` |
| `mainOrder.totalGreaterThan` | Order 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.exists`           | Previous step created an order  | None                     |
| `lastOrder.hasProduct`       | Order contains specific product | `productId`, `variantId` |
| `lastOrder.totalGreaterThan` | Order total exceeds amount      | `amount` (in cents)      |

### Offer Conditions

Check **offer acceptance** from the previous step:

| Condition              | Description                 | Parameters |
| ---------------------- | --------------------------- | ---------- |
| `offer.accepted`       | Customer accepted the offer | None       |
| `offer.declined`       | Customer declined the offer | None       |
| `offer.acceptedWithId` | Accepted specific offer     | `offerId`  |

### Payment Conditions

Check **payment outcome** from the previous step:

| Condition                   | Description            | Parameters          |
| --------------------------- | ---------------------- | ------------------- |
| `payment.success`           | Payment was successful | None                |
| `payment.failed`            | Payment failed         | None                |
| `payment.amountGreaterThan` | Payment exceeds amount | `amount` (in cents) |

### Customer Conditions

Check **customer attributes** (tags assigned via checkout or CRM):

| Condition               | Description                    | Parameters                     |
| ----------------------- | ------------------------------ | ------------------------------ |
| `customer.hasTag`       | Customer has specific tag      | `tag` (e.g., "segment:vip")    |
| `customer.hasTagPrefix` | Has any tag with prefix        | `prefix` (e.g., "utm\_source") |
| `customer.hasAnyTag`    | Has ANY of selected tags (OR)  | `tags` (comma-separated)       |
| `customer.hasAllTags`   | Has ALL of selected tags (AND) | `tags` (comma-separated)       |

### Advanced: JSONLogic

For complex conditions, use **Advanced Mode** in the Step Editor:

```json theme={null}
// 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 } }
      ]
    }
  ]
}
```

<Info>
  **Tip**: The Advanced Mode editor in the CRM provides syntax validation and a quick reference guide!
</Info>

***

## Migration from Legacy

If you're upgrading from the old system:

### Before (Legacy)

```tsx theme={null}
await funnel.next({
  data: {
    orderId: '123',
    orderAmount: 100,
    customerId: '456'
  }
});
```

### After (Resource System v2)

```tsx theme={null}
await funnel.next({
  data: {
    resources: {
      order: { id: '123', amount: 100 },
      customer: { id: '456' }
    }
  }
});
```

<Info>
  **Good News**: The orchestrator auto-migrates legacy data, so old plugins still work!
</Info>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Plugin API Reference" icon="book" href="/developer-tools/sdk/api">
    Complete API documentation for all hooks and methods
  </Card>

  <Card title="Examples" icon="code" href="/developer-tools/sdk/examples">
    Full plugin examples with source code
  </Card>

  <Card title="Manifest Guide" icon="file-code" href="/developer-tools/plugins/manifest">
    Learn how to configure your plugin manifest
  </Card>

  <Card title="Deployment" icon="rocket" href="/developer-tools/plugins/deployment">
    Deploy your plugin to production
  </Card>
</CardGroup>
