Skip to main content

Plugin Manifest Guide

The plugin.manifest.json is the contract between your plugin and the TagadaPay platform. It defines what your plugin does, what data it needs, and what data it provides to other steps in a funnel.

Why the Manifest Matters

The manifest is critical for the Funnel Orchestrator to work correctly:
  1. Parameter Propagation: Tells the orchestrator what data your plugin needs (requirements) and what data it provides (resources)
  2. Type Safety: Ensures data flows correctly between funnel steps
  3. URL Building: Defines pages and their paths for routing
  4. Feature Detection: Allows the platform to discover what your plugin can do
Without a proper manifest, the orchestrator cannot resolve parameters or route users correctly through your funnel.

Basic Structure

{
  "name": "My Awesome Plugin",
  "pluginId": "my-plugin",
  "description": "A brief description of what your plugin does",
  "version": "1.0.0",
  "author": "Your Name",
  "mode": "direct-mode",
  "category": "checkout",
  "tags": ["checkout", "payment"],
  "configuration": {
    "schema": "./config/schema.json",
    "uiSchema": "./config/ui-schema.json",
    "presets": []
  },
  "pages": [],
  "router": {
    "basePath": "/",
    "matcher": ".*",
    "excluder": null
  },
  "requirements": {
    "sdk": "^2.2.0",
    "sdkVersion": "v2"
  }
}

Key Fields

  • name: Display name shown in the CRM
  • pluginId: Unique identifier for your plugin
  • mode: "direct-mode" for modern V2 plugins
  • category: Plugin category (checkout, upsell, thankyou, custom)
  • configuration: References to configuration schemas and presets
  • router: Routing configuration for the plugin
  • requirements: SDK version requirements

Configuration System

The configuration system allows you to define customizable settings for your plugin that can be edited in the CRM.

Configuration Structure

{
  "configuration": {
    "schema": "./config/schema.json",
    "uiSchema": "./config/ui-schema.json",
    "presets": [
      {
        "id": "default",
        "name": "Default",
        "description": "Default checkout configuration",
        "config": "./config/default.config.json"
      },
      {
        "id": "custom-theme",
        "name": "Custom Theme",
        "description": "Custom branded theme",
        "config": "./config/custom-theme.config.json"
      }
    ]
  }
}

Configuration Files

schema.json - JSON Schema

Defines the structure and validation rules for your plugin configuration:
{
  "type": "object",
  "properties": {
    "branding": {
      "type": "object",
      "properties": {
        "primaryColor": {
          "type": "string",
          "format": "color"
        }
      }
    }
  }
}

ui-schema.json - UI Hints

Provides hints for rendering the configuration editor in the CRM:
{
  "branding": {
    "ui:order": ["primaryColor", "logo"],
    "primaryColor": {
      "ui:widget": "color"
    }
  }
}

Preset Configs

Preset configuration files contain complete plugin configurations that users can select in the CRM:
{
  "branding": {
    "primaryColor": "#3B82F6",
    "logo": "https://example.com/logo.png"
  },
  "layout": {
    "template": "modern"
  }
}
The CRM automatically loads and validates these configurations when deploying your plugin.

Static Resources System

Static resources allow you to configure plugin-specific data (like offer IDs, product IDs, etc.) in the CRM’s funnel editor that can be accessed at runtime.

What Are Static Resources?

Static resources are configuration values that:
  • Are defined in the funnel step configuration in the CRM
  • Can be different per deployment/funnel
  • Are accessible via context.static in your plugin code
  • Don’t require URL parameters

Defining Static Resources in Manifest

Use type: "static" in your requirements:
{
  "path": "/offer",
  "features": [
    {
      "type": "offer",
      "requirements": [
        {
          "resource": "offer",
          "from": [
            {
              "name": "id",
              "type": "static"
            }
          ]
        }
      ]
    }
  ]
}
This tells the orchestrator: “This page needs an offer ID from static configuration.”

Accessing Static Resources in Code

import { useFunnel } from '@tagadapay/plugin-sdk/v2';

function OfferPage() {
  const { context } = useFunnel();

  // Access static resources
  const offerId = context?.static?.offer?.id;

  return <div>Offer ID: {offerId}</div>;
}

Local Development: resources.static.json

For local development, create /config/resources.static.json to mock static resources:
{
  "offer": {
    "id": "offer_ad2e64728dd8"
  },
  "product": {
    "id": "product_abc123"
  }
}
Important: This file is ONLY loaded in local development (localhost, *.localhost, ngrok). Values from the backend (CRM) always take precedence. Do NOT deploy this file to production.

Example Use Cases

Single Offer Upsell Page

{
  "path": "/upsell-special",
  "features": [
    {
      "type": "offer",
      "requirements": [
        {
          "resource": "order",
          "from": [{ "name": "id", "type": "context" }]
        },
        {
          "resource": "offer",
          "from": [{ "name": "id", "type": "static" }]
        }
      ]
    }
  ]
}
Configure in CRM: Set offer.id = "offer_abc123" for this step. The page will always show this specific offer.

Multi-Product Bundle Page

{
  "path": "/bundle",
  "features": [
    {
      "type": "custom",
      "requirements": [
        {
          "resource": "products",
          "from": [{ "name": "ids", "type": "static" }]
        }
      ]
    }
  ]
}
Configure in CRM: Set products.ids = ["prod_1", "prod_2", "prod_3"]. Your plugin receives all product IDs.

Pages Definition

Pages define the routes your plugin exposes. The orchestrator uses this to build URLs and route users.

Page Structure

{
  "path": "/checkout",
  "features": [
    {
      "type": "checkout",
      "requirements": []
    }
  ],
  "remappable": true
}

Key Fields

  • path: URL path for this page (can include path parameters like :orderId)
  • features: Array of features this page provides
  • remappable: Whether this page can be remapped to a different URL in the funnel editor

Resource Requirement Types

The modern manifest uses a flexible requirements array with a from field that specifies where data comes from:

1. Query Parameters (type: "query")

Data passed in the URL query string:
{
  "path": "/checkout",
  "features": [
    {
      "type": "checkout",
      "requirements": [
        {
          "resource": "checkoutSession",
          "from": [
            {
              "name": "checkoutToken",
              "type": "query"
            }
          ]
        }
      ]
    }
  ],
  "remappable": true
}
Result: /checkout?checkoutToken=tok_abc123

2. Path Parameters (type: "path")

Data embedded in the URL path:
{
  "path": "/thankyou/:orderId",
  "features": [
    {
      "type": "thankyou",
      "requirements": [
        {
          "resource": "order",
          "from": [
            {
              "name": "id",
              "type": "path"
            }
          ]
        }
      ]
    }
  ],
  "remappable": true
}
Result: /thankyou/ord_abc123

3. Context Resources (type: "context")

Data from previous funnel steps (stored in session):
{
  "path": "/offer",
  "features": [
    {
      "type": "offer",
      "requirements": [
        {
          "resource": "order",
          "from": [
            {
              "name": "id",
              "type": "context"
            }
          ]
        }
      ]
    }
  ],
  "remappable": true
}
The orchestrator automatically resolves order.id from the session and includes it in the URL.

4. Static Resources (type: "static")

Data configured in the CRM funnel editor (not in URL):
{
  "path": "/offerfromstatic",
  "features": [
    {
      "type": "offer",
      "requirements": [
        {
          "resource": "order",
          "from": [
            {
              "name": "id",
              "type": "context"
            }
          ]
        },
        {
          "resource": "offer",
          "from": [
            {
              "name": "id",
              "type": "static"
            }
          ]
        }
      ]
    }
  ],
  "remappable": true
}
Accessing in code:
const { context } = useFunnel();
const offerId = context?.static?.offer?.id; // From CRM configuration
const orderId = context?.order?.id; // From session

Multi-Page Example (Complete Funnel)

{
  "pages": [
    {
      "path": "/checkout",
      "features": [
        {
          "type": "checkout",
          "requirements": [
            {
              "resource": "checkoutSession",
              "from": [
                {
                  "name": "checkoutToken",
                  "type": "query"
                }
              ]
            }
          ]
        }
      ],
      "remappable": true
    },
    {
      "path": "/offer",
      "features": [
        {
          "type": "offer",
          "requirements": [
            {
              "resource": "order",
              "from": [
                {
                  "name": "id",
                  "type": "context"
                }
              ]
            },
            {
              "resource": "offer",
              "from": [
                {
                  "name": "id",
                  "type": "static"
                }
              ]
            }
          ]
        }
      ],
      "remappable": true
    },
    {
      "path": "/thankyou/:orderId",
      "features": [
        {
          "type": "thankyou",
          "requirements": [
            {
              "resource": "order",
              "from": [
                {
                  "name": "id",
                  "type": "path"
                }
              ]
            }
          ]
        }
      ],
      "remappable": true
    }
  ]
}

Requirements: What Your Plugin Needs

Requirements tell the orchestrator what data your plugin needs to function. The orchestrator will:
  1. Check if this data exists based on the type (query, path, context, static)
  2. Resolve it from the appropriate source
  3. Make it available to your plugin via the SDK

Requirement Structure

Each requirement has:
  • resource: The name of the resource (e.g., "order", "offer", "checkoutSession")
  • from: Array of sources that can provide this data
{
  "requirements": [
    {
      "resource": "order",
      "from": [
        {
          "name": "id",
          "type": "context"
        }
      ]
    }
  ]
}

Data Flow by Type

Query Parameters (type: "query")

{
  "resource": "checkoutSession",
  "from": [
    {
      "name": "checkoutToken",
      "type": "query"
    }
  ]
}
URL: /checkout?checkoutToken=tok_abc123
Access: Automatically handled by SDK, creates checkout session

Path Parameters (type: "path")

{
  "resource": "order",
  "from": [
    {
      "name": "id",
      "type": "path"
    }
  ]
}
URL: /thankyou/:orderId/thankyou/ord_abc123
Access: Via context.order.id in your plugin

Context Resources (type: "context")

{
  "resource": "order",
  "from": [
    {
      "name": "id",
      "type": "context"
    }
  ]
}
Source: Previous funnel steps (stored in session)
Access: Via context.order.id in your plugin

Static Resources (type: "static")

{
  "resource": "offer",
  "from": [
    {
      "name": "id",
      "type": "static"
    }
  ]
}
Source: CRM funnel editor configuration
Access: Via context.static.offer.id in your plugin
The orchestrator automatically resolves all requirements before loading your page, ensuring all required data is available.

Multiple Requirements

A page can have multiple requirements from different sources:
{
  "path": "/offerfromstatic",
  "features": [
    {
      "type": "offer",
      "requirements": [
        {
          "resource": "order",
          "from": [
            {
              "name": "id",
              "type": "context"
            }
          ]
        },
        {
          "resource": "offer",
          "from": [
            {
              "name": "id",
              "type": "static"
            }
          ]
        }
      ]
    }
  ]
}
This page needs:
  • An order.id from the session (previous checkout step)
  • An offer.id from static configuration (CRM editor)

Resource Production: What Your Plugin Provides

Your plugin can produce resources that become available to subsequent funnel steps. While not explicitly declared in the modern manifest format, you emit resources via the SDK.

Emitting Resources

When your plugin completes an action, emit resources via the funnel SDK:
import { useFunnel } from '@tagadapay/plugin-sdk/v2';

function CheckoutPage() {
  const funnel = useFunnel();

  const handleCheckoutComplete = async (orderData) => {
    // Navigate to next step with resources
    await funnel.next({
      resources: {
        order: {
          id: orderData.id,
          total: orderData.total,
          customer: {
            email: orderData.customer.email,
            id: orderData.customer.id
          }
        }
      }
    });
  };

  return <CheckoutForm onComplete={handleCheckoutComplete} />;
}
The orchestrator automatically stores these resources in the session and makes them available to subsequent steps.

Accessing Resources in Next Steps

Resources emitted by previous steps are available via context:
function ThankYouPage() {
  const { context } = useFunnel();

  // Access order from previous checkout step
  const orderId = context?.order?.id;
  const orderTotal = context?.order?.total;
  const customerEmail = context?.order?.customer?.email;

  return (
    <div>
      <h1>Thank you for your order!</h1>
      <p>Order ID: {orderId}</p>
      <p>Total: ${orderTotal}</p>
      <p>Confirmation sent to: {customerEmail}</p>
    </div>
  );
}

Resource Naming Best Practices

Use semantic, descriptive names:
// ✅ Good: Clear intent
resources: {
  order: { id: 'ord_123' },
  mainOrder: { id: 'ord_123' }, // For persistence
  upsellOrder: { id: 'ord_456' }
}

// ❌ Bad: Unclear naming
resources: {
  order1: { id: 'ord_123' },
  order2: { id: 'ord_456' }
}

Remappable Pages

The remappable property determines whether a page can be remapped to a different URL in the funnel editor.
{
  "path": "/checkout",
  "features": [
    {
      "type": "checkout",
      "requirements": []
    }
  ],
  "remappable": true
}

When to Use Remappable

remappable: true - Most pages should be remappable:
  • Checkout pages
  • Upsell pages
  • Thank you pages
  • Offer pages
This allows users to customize URLs in the funnel editor (e.g., /checkout/buy-now). remappable: false - Use for special routes:
  • Wildcard routes (/club/*)
  • API endpoints
  • Utility pages
{
  "path": "/club/*",
  "features": [
    {
      "type": "custom",
      "requirements": [
        {
          "resource": "club"
        }
      ]
    }
  ],
  "remappable": false
}
When a page is remapped, the SDK’s path remapping system automatically handles the URL translation. See the Path Remapping Guide for details.

How the Orchestrator Uses the Manifest

1. Parameter Resolution

When navigating to a step, the orchestrator:
// Step URL template: /thankyou/:orderId
// Step requirements: { orderId: "string" }

// Orchestrator checks session for "orderId"
// If not found, looks for "order.id" in resources
// If found, resolves: /thankyou/ord_123

2. URL Building

The orchestrator builds URLs using:
  • Mount Point ID: The funnel alias (e.g., my-funnel--store123.cdn.tagadapay.com)
  • Page Path: From manifest (e.g., /thankyou/:orderId)
  • Path Parameters: Resolved based on requirement type (query, path, context)
  • Static Resources: Loaded from CRM configuration (not in URL)
Examples:
// Query parameter requirement
// URL: /checkout?checkoutToken=tok_abc123

// Path parameter requirement
// URL: /thankyou/ord_123

// Context requirement
// Orchestrator resolves from session, adds to URL: /offer?orderId=ord_123

// Static requirement
// Loaded from CRM config, accessed via context.static (not in URL)

3. Resource Propagation

The orchestrator maintains a session with all resources:
// After checkout completes
context = {
  order: { id: 'ord_123', total: 99.99 },
  static: {
    /* CRM configuration */
  },
};

// After upsell completes
context = {
  order: { id: 'ord_123', total: 99.99 },
  upsellOrder: { id: 'ord_456', total: 29.99 },
  static: {
    /* CRM configuration */
  },
};

// All resources available to subsequent steps via useFunnel()!

Resource Naming Strategy

Use Descriptive Names

Choose names that clearly indicate what the resource represents:
// ✅ Good: Clear and semantic
resources: {
  order: { id: 'ord_123' },           // Current/hot order
  mainOrder: { id: 'ord_123' },       // Original checkout order
  upsellOrder: { id: 'ord_456' },     // Upsell order
  downsellOrder: { id: 'ord_789' }    // Downsell order
}

// ❌ Bad: Unclear naming
resources: {
  order: { id: 'ord_123' },
  order2: { id: 'ord_456' },
  order3: { id: 'ord_789' }
}

Generic vs Specific Resources

Generic Resources (Hot Context):
  • Use common names like order, product, customer
  • Represent the “current” item in focus
  • Can be overwritten by subsequent steps
// Checkout emits
resources: {
  order: {
    id: 'ord_123';
  }
}

// Upsell also emits (overwrites)
resources: {
  order: {
    id: 'ord_456';
  }
}
Specific Resources (Persistent):
  • Use unique names like mainOrder, upsellOrder
  • Never get overwritten
  • Available for the entire funnel session
// Better: Both generic and specific
resources: {
  order: { id: 'ord_123' },      // Hot - for next step
  mainOrder: { id: 'ord_123' }   // Persistent - for later
}

Advanced Patterns

Multiple Path Parameters

Combine multiple path parameters with context requirements:
{
  "path": "/order/:orderId/upsell/:productId",
  "features": [
    {
      "type": "upsell",
      "requirements": [
        {
          "resource": "order",
          "from": [{ "name": "id", "type": "path" }]
        },
        {
          "resource": "product",
          "from": [{ "name": "id", "type": "path" }]
        }
      ]
    }
  ]
}
Result: /order/ord_123/upsell/prod_456

Combining Query and Context

Mix query parameters (user input) with context (session data):
{
  "path": "/thankyou",
  "features": [
    {
      "type": "thankyou",
      "requirements": [
        {
          "resource": "order",
          "from": [{ "name": "id", "type": "context" }]
        },
        {
          "resource": "utm",
          "from": [{ "name": "source", "type": "query" }]
        }
      ]
    }
  ]
}
Result: /thankyou?orderId=ord_123&utm_source=facebook

Complete Example: Multi-Step Funnel

Native Checkout Plugin (Modern Format)

{
  "name": "Native Checkout",
  "pluginId": "native-checkout",
  "description": "Modern checkout with multiple page types",
  "version": "2.0.0",
  "author": "TagadaPay Team",
  "mode": "direct-mode",
  "category": "checkout",
  "tags": ["checkout", "native", "v2"],
  "configuration": {
    "schema": "./config/schema.json",
    "uiSchema": "./config/ui-schema.json",
    "presets": [
      {
        "id": "default",
        "name": "Default",
        "description": "Default checkout configuration",
        "config": "./config/default.config.json"
      }
    ]
  },
  "pages": [
    {
      "path": "/checkout",
      "features": [
        {
          "type": "checkout",
          "requirements": [
            {
              "resource": "checkoutSession",
              "from": [
                {
                  "name": "checkoutToken",
                  "type": "query"
                }
              ]
            }
          ]
        }
      ],
      "remappable": true
    },
    {
      "path": "/offer",
      "features": [
        {
          "type": "offer",
          "requirements": [
            {
              "resource": "order",
              "from": [
                {
                  "name": "id",
                  "type": "context"
                }
              ]
            }
          ]
        }
      ],
      "remappable": true
    },
    {
      "path": "/offerfromstatic",
      "features": [
        {
          "type": "offer",
          "requirements": [
            {
              "resource": "order",
              "from": [
                {
                  "name": "id",
                  "type": "context"
                }
              ]
            },
            {
              "resource": "offer",
              "from": [
                {
                  "name": "id",
                  "type": "static"
                }
              ]
            }
          ]
        }
      ],
      "remappable": true
    },
    {
      "path": "/thankyou/:orderId",
      "features": [
        {
          "type": "thankyou",
          "requirements": [
            {
              "resource": "order",
              "from": [
                {
                  "name": "id",
                  "type": "path"
                }
              ]
            }
          ]
        }
      ],
      "remappable": true
    },
    {
      "path": "/club/*",
      "features": [
        {
          "type": "custom",
          "requirements": [
            {
              "resource": "club"
            }
          ]
        }
      ],
      "remappable": false
    }
  ],
  "router": {
    "basePath": "/",
    "matcher": ".*",
    "excluder": null
  },
  "requirements": {
    "sdk": "^2.2.0",
    "sdkVersion": "v2"
  }
}

How Data Flows Through This Funnel

Step 1: Checkout

// User navigates to: /checkout?checkoutToken=tok_123

// Requirement: checkoutSession from query parameter
// SDK automatically creates checkout session

// On completion, plugin emits resources:
funnel.next({
  resources: {
    order: {
      id: 'ord_123',
      total: 99.99,
      customer: { email: '[email protected]', id: 'cus_456' },
    },
  },
});

Step 2: Dynamic Offer (Context)

// Orchestrator navigates to: /offer?orderId=ord_123

// Requirement: order.id from context (previous step)
// Orchestrator resolves: context.order.id = 'ord_123'

// In plugin:
const { context } = useFunnel();
const orderId = context?.order?.id; // 'ord_123'

// On completion:
funnel.next({
  resources: {
    upsellOrder: { id: 'ord_456', total: 29.99 },
  },
});

Step 3: Static Offer (Static + Context)

// Orchestrator navigates to: /offerfromstatic?orderId=ord_123

// Requirements:
// 1. order.id from context (previous checkout)
// 2. offer.id from static (CRM configuration)

// In plugin:
const { context } = useFunnel();
const orderId = context?.order?.id; // 'ord_123' from session
const offerId = context?.static?.offer?.id; // 'offer_abc' from CRM

// Show specific offer configured in CRM for this order

Step 4: Thank You

// Orchestrator navigates to: /thankyou/ord_123

// Requirement: order.id as path parameter
// Orchestrator resolves from context and embeds in URL

// In plugin:
const { context } = useFunnel();
const order = context?.order; // Full order data available
const upsellOrder = context?.upsellOrder; // Also available!

// All previous resources are accessible
The orchestrator handles ALL parameter resolution, URL building, and resource propagation automatically!

Key Takeaways

  1. Query parameters (type: "query") - User provides in URL
  2. Path parameters (type: "path") - Orchestrator embeds in URL from context
  3. Context resources (type: "context") - From previous steps, available in session
  4. Static resources (type: "static") - From CRM configuration, not in URL
All resources from all previous steps remain available in context for the entire funnel session.

Validation & Best Practices

✅ Always Specify Resource Type

Be explicit about where data comes from:
{
  "requirements": [
    {
      "resource": "order",
      "from": [
        {
          "name": "id",
          "type": "context"
        }
      ]
    }
  ]
}
Clear type specification helps the orchestrator resolve data correctly.

❌ Don’t Mix Resource Types Incorrectly

// ❌ Bad: Using query for session data
{
  "resource": "order",
  "from": [
    {
      "name": "id",
      "type": "query"  // Wrong! order.id should be from context
    }
  ]
}

// ✅ Good: Use context for session data
{
  "resource": "order",
  "from": [
    {
      "name": "id",
      "type": "context"
    }
  ]
}

✅ Use Static Resources for Configuration

Use static resources for values that should be configured in the CRM:
{
  "path": "/special-offer",
  "features": [
    {
      "type": "offer",
      "requirements": [
        {
          "resource": "offer",
          "from": [{ "name": "id", "type": "static" }]
        },
        {
          "resource": "order",
          "from": [{ "name": "id", "type": "context" }]
        }
      ]
    }
  ]
}
This allows different funnels to show different offers without code changes.

✅ Make Pages Remappable

Most pages should be remappable for flexibility:
{
  "path": "/checkout",
  "features": [{ "type": "checkout", "requirements": [] }],
  "remappable": true
}
Only set remappable: false for wildcard routes or special functionality.

✅ Use Semantic Resource Names

// ✅ Good: Clear intent
resources: {
  mainOrder: { id: 'ord_123' },
  upsellOrder: { id: 'ord_456' },
  customer: { email: '[email protected]' }
}

// ❌ Bad: Unclear
resources: {
  order1: { id: 'ord_123' },
  order2: { id: 'ord_456' },
  data: { email: '[email protected]' }
}

✅ Test with resources.static.json

For local development, create /config/resources.static.json:
{
  "offer": {
    "id": "offer_test123"
  },
  "product": {
    "id": "prod_test456"
  }
}
This lets you test static resource pages without configuring them in the CRM.

Debugging

Debug Context and Resources

Log the full context to see what’s available:
import { useFunnel } from '@tagadapay/plugin-sdk/v2';

function MyPage() {
  const { context } = useFunnel();

  // Debug: See all available data
  console.log('[MyPage] Full context:', context);
  console.log('[MyPage] Order:', context?.order);
  console.log('[MyPage] Static resources:', context?.static);

  return <div>...</div>;
}

Debug Static Resources

Check if your static resources are loading correctly:
const { context } = useFunnel();

console.log('[Debug] Static offer ID:', context?.static?.offer?.id);
console.log('[Debug] Is localhost?', window.location.hostname === 'localhost');
Remember: resources.static.json only works in development (localhost, *.localhost, ngrok). In production, static resources come from the CRM configuration.

Check Requirement Types

Verify requirements are resolving correctly:
// Query parameter (checkoutToken)
console.log('[Debug] Query checkoutToken:', new URLSearchParams(window.location.search).get('checkoutToken'));

// Path parameter (orderId)
console.log('[Debug] Path orderId:', window.location.pathname.split('/').pop());

// Context resource (order from previous step)
console.log('[Debug] Context order:', context?.order);

// Static resource (from CRM config)
console.log('[Debug] Static offer:', context?.static?.offer);

Backend Logs

Check backend logs for orchestrator decisions:
[Orchestrator] Resolving requirements for step: /thankyou/:orderId
[Orchestrator] Requirement: order.id from type:path
[Orchestrator] Found in context.order.id: ord_123
[Orchestrator] Built URL: /thankyou/ord_123

[Orchestrator] Resolving requirements for step: /offerfromstatic
[Orchestrator] Requirement: order.id from type:context -> ord_123
[Orchestrator] Requirement: offer.id from type:static -> offer_abc
[Orchestrator] Static resources loaded from CRM config

Summary

The manifest is your plugin’s contract with the platform:

Core Concepts

  1. Configuration System - Define customizable settings with schemas and presets
  2. Static Resources - Configure plugin-specific data in the CRM (accessible via context.static)
  3. Define Pages - Specify routes, features, and whether they’re remappable
  4. Declare Requirements - Specify data needs using type (query, path, context, static)
  5. Emit Resources - Produce data via funnel.next() for subsequent steps
  6. Trust the Orchestrator - It handles parameter resolution, URL building, and resource propagation

Requirement Types Quick Reference

TypeSourceIn URL?Access Via
queryURL query params✅ Yes (query param)Auto-handled by SDK
pathURL path segments✅ Yes (path param)context.{resource}
contextPrevious steps🔄 Optional (via session)context.{resource}
staticCRM configuration❌ No (server-side only)context.static.{resource}
Context Resources Explained:
  • Context resources are stored in the funnel session on the server
  • They’re available via context.{resource} without needing to be in the URL
  • The orchestrator may optionally include them in the URL for specific use cases (like when a path requires :orderId)

Development Files

  • /config/schema.json - Configuration structure
  • /config/ui-schema.json - CRM editor UI hints
  • /config/*.config.json - Preset configurations
  • /config/resources.static.json - Local dev only - Mock static resources
A well-defined manifest = Seamless funnel orchestration with static resources ✨

Next Steps