Skip to content

Latest commit

 

History

History
1829 lines (1631 loc) · 55.3 KB

MCP_BUSINESS_FLOWS.md

File metadata and controls

1829 lines (1631 loc) · 55.3 KB

MCP Business Flows — Real Scenarios with Tool Synergies

Audience: AI agents using agentstack.execute via MCP
How to use: Find the scenario closest to your task, adapt the JSON, run it.
All actions are valid step.action values unless marked REST-only.
Full action catalog: GET /mcp/actions

AgentStack platform billing (two planes)

Scenarios below use tenant-app buff SKUs (your product tiers). Do not conflate with AgentStack platform planes:

Plane Tiers entity_kind
Personal (user) free, premium ($15), vip ($45) user
Business (project) sandbox → launch → business → scale → enterprise project

Personal Premium/VIP never raises project API/member/sandbox quotas. Platform upgrade: https://agentstack.tech/pricing · SoT: shared/subscription/ · MCP copy: shared/mcp/subscription_plane_copy.py

scheduler.create_task accepts cron_expression as cron, run_at as execute_at (now is the current UTC time), and task_type once / cron as one_time / recurring. payload.action runs only when it is a registered scheduler action (storage.cleanup_temp, payments.reconcile_pending, and the list returned as registered_scheduler_actions). Any other name is stored and does nothing when the task fires. payments.create charges card, stripe, or tochka (payment_method is required) and returns status: pending. A same-batch if on completed skips the following steps; use pending to continue after the charge is accepted, then payments.get for a settled completed. A wallet credit is wallets.deposit. Currencies are USD, EUR, RUB, BRL. The payer is the session user. Step if may be {"from": "step.result.field", "op": "equals", "value": ...} or "op": "exists". A buff effect key without a data. prefix is stored as data.<key> (xp_multiplier → data.xp_multiplier). $user_id and $project_id are filled from context when that key is set; a whole $name keeps the context type. Other $tokens stay literal until the caller replaces them. Chain a previous step with {"from": "step.result.field"}, not $step_field. A list row is result.contacts[0].id (or result.contacts.0.id).


Index

  1. Complete Project Bootstrap
  2. SaaS Trial → Subscription → Upgrade
  3. Game Economy — Currencies, Wallets, Rewards
  4. Loyalty & Rewards Program
  5. E-commerce Checkout Flow
  6. AI Automation Pipeline
  7. Content Platform with Access Control
  8. Social Community Activation
  9. Multi-tenant B2B Onboarding
  10. Event-driven Marketing Campaign
  11. Usage-based Billing with Auto-throttling
  12. Player Matchmaking & Seasonal Events
  13. Read-only session bootstrap
  14. Seller one-click
  15. Generation safe mutation + canary promote
  16. Business composite bootstrap
  17. Services inquiry + downstream MCP
  18. Platform grant + tenant subscription monetization

1. Complete Project Bootstrap

Problem: Start from zero — provision a project with economy, RBAC, logic rules, API keys, and scheduled maintenance in a single session.

Tool chain: projects.create_project_anonymous → assets.create ×N → wallets.create → wallets.deposit → buffs.create_buff (welcome) → logic.create (rules) → scheduler.create_task (maintenance) → apikeys.create (restricted) → rbac.assign_role

{
  "steps": [
    {
      "id": "project",
      "action": "projects.create_project_anonymous",
      "params": {
        "name": "My App",
        "data": {
          "config": {"currency": "credits", "version": "1.0"}
        }
      }
    },
    {
      "id": "asset_credits",
      "action": "assets.create",
      "params": {
        "name": "Credits",
        "type": "currency",
        "project_id": {"from": "project.result.project_id"},
        "data": {"symbol": "CR", "decimals": 0}
      }
    },
    {
      "id": "asset_premium",
      "action": "assets.create",
      "params": {
        "name": "Premium Gems",
        "type": "currency",
        "project_id": {"from": "project.result.project_id"},
        "data": {"symbol": "GEM", "decimals": 0, "premium": true}
      }
    },
    {
      "id": "wallet",
      "action": "wallets.create",
      "params": {
        "name": "Main Wallet",
        "currency": "credits",
        "project_id": {"from": "project.result.project_id"}
      }
    },
    {
      "id": "seed_balance",
      "action": "wallets.deposit",
      "params": {
        "wallet_id": {"from": "wallet.result.wallet_id"},
        "amount": 1000,
        "description": "Initial seed balance"
      }
    },
    {
      "id": "welcome_buff",
      "action": "buffs.create_buff",
      "params": {
        "name": "Welcome Bonus",
        "description": "7-day welcome period with boosted rates",
        "duration_days": 7,
        "effects": {"xp_multiplier": 2.0, "welcome": true},
        "revert_on_expire": true,
        "project_id": {"from": "project.result.project_id"}
      }
    },
    {
      "id": "daily_reward_rule",
      "action": "logic.create",
      "params": {
        "name": "Daily Login Reward",
        "description": "Award credits on daily login",
        "triggers": [{"type": "command", "command": "event.daily_login"}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "add",
            "parameters": {"field1": "user.data.credits", "field2": 10, "result_field": "user.data.credits"}
          }
        ],
        "enabled": true,
        "project_id": {"from": "project.result.project_id"}
      }
    },
    {
      "id": "cleanup_task",
      "action": "scheduler.create_task",
      "params": {
        "name": "Weekly Cleanup",
        "task_type": "cron",
        "cron_expression": "0 3 * * 0",
        "payload": {"action": "storage.cleanup_temp"},
        "project_id": {"from": "project.result.project_id"}
      }
    },
    {
      "id": "agent_key",
      "action": "apikeys.create",
      "params": {
        "name": "AI Agent Key",
        "service_caps": ["payments", "scheduler"],
        "project_id": {"from": "project.result.project_id"}
      }
    }
  ]
}

Key synergies:

  • project_id flows from step 1 through all subsequent steps via {"from": "..."}
  • assets.create x2 = multi-currency economy in one batch
  • buffs.create_buff without apply = template ready for all new users
  • apikeys.create with service_caps = safe agent key that can pay + schedule

2. SaaS Trial → Subscription → Upgrade

Problem: User starts a free trial, converts to paid, and later upgrades their plan. Each transition must be atomic with automatic revert on failure.

Phase A — Start Trial

{
  "steps": [
    {
      "id": "trial_buff",
      "action": "buffs.create_buff",
      "params": {
        "name": "Pro Trial",
        "duration_days": 14,
        "effects": {
          "plan": "pro",
          "api_calls_limit": 10000,
          "team_members_limit": 5
        },
        "revert_on_expire": true,
        "on_expire": [{"command": "event.trial_expired", "payload": {"user_id": "$user_id"}}]
      }
    },
    {
      "id": "apply_trial",
      "action": "buffs.apply_buff",
      "params": {
        "buff_id": {"from": "trial_buff.result.buff_id"},
        "entity_id": "$user_id",
        "entity_kind": "user"
      }
    },
    {
      "id": "trial_reminder",
      "action": "scheduler.create_task",
      "params": {
        "name": "Trial expiry reminder",
        "task_type": "once",
        "run_at": "$trial_end_minus_2days",
        "payload": {"template": "trial_expiring_soon", "user_id": "$user_id"}
      }
    }
  ]
}

Phase B — Convert to Paid (three-rail)

Canonical (tenant SaaS): buffs.create_buff (SKU template) → commerce.sell.activate → payments.create (intent=subscription, metadata.recipient_project_id) → webhook/C24 auto-grant. Manual comp: buffs.grant_tenant_subscription. Do not chain payments.create → buffs.apply_buff.

{
  "steps": [
    {
      "id": "remove_trial",
      "action": "buffs.revert_buff",
      "params": {
        "application_id": "$trial_application_id",
        "entity_id": "$user_id",
        "entity_kind": "user"
      }
    },
    {
      "id": "go_live",
      "action": "commerce.sell.activate",
      "params": {
        "project_id": "$tenant_project_id",
        "subscription_entitlement": true,
        "preset_inputs": {"buff_template_id": "$pro_buff_template_id"}
      }
    },
    {
      "id": "charge",
      "action": "payments.create",
      "params": {
        "amount": 29.0,
        "currency": "USD",
        "intent": "subscription",
        "metadata": {
          "plan": "pro_monthly",
          "recipient_project_id": "$tenant_project_id"
        }
      }
    },
    {
      "id": "verify_grant",
      "action": "buffs.list_active_buffs",
      "params": {
        "entity_id": "$user_id",
        "entity_kind": "user",
        "project_id": "$tenant_project_id"
      }
    },
    {
      "id": "renewal_task",
      "action": "scheduler.create_task",
      "params": {
        "name": "Monthly renewal",
        "task_type": "once",
        "run_at": "$next_billing_date",
        "payload": {"action": "process_renewal", "user_id": "$user_id", "plan": "pro_monthly"}
      }
    }
  ]
}

Phase C — Plan Upgrade (Pro → Enterprise)

Paid upgrade: new checkout with upgraded SKU → webhook/C24 grant. Manual/support: buffs.grant_tenant_subscription with new plan_id. Platform operator tiers: buffs.grant_subscription (PID=1).

{
  "steps": [
    {
      "id": "prorate_charge",
      "action": "payments.create",
      "params": {
        "amount": 71.0,
        "currency": "USD",
        "intent": "subscription",
        "metadata": {
          "from_plan": "pro_monthly",
          "to_plan": "enterprise_monthly",
          "recipient_project_id": "$tenant_project_id"
        }
      }
    },
    {
      "id": "cancel_pro",
      "action": "buffs.cancel_buff",
      "params": {
        "application_id": "$pro_application_id",
        "entity_id": "$user_id",
        "entity_kind": "user"
      }
    },
    {
      "id": "verify_upgrade",
      "action": "buffs.list_active_buffs",
      "params": {
        "entity_id": "$user_id",
        "entity_kind": "user",
        "project_id": "$tenant_project_id"
      }
    }
  ]
}

Key synergies:

  • payments.create + if condition = payment-gated buff activation
  • Paid upgrade: new checkout → webhook/C24 grant; manual: buffs.grant_tenant_subscription with new plan_id
  • scheduler.create_task for each future event = event-driven billing

3. Game Economy — Currencies, Wallets, Rewards

Problem: Set up a game economy with soft/hard currencies, reward logic, daily bonuses, and in-app purchases.

{
  "steps": [
    {
      "id": "soft_currency",
      "action": "assets.create",
      "params": {"name": "Gold", "type": "currency", "data": {"symbol": "G", "earnable": true}}
    },
    {
      "id": "hard_currency",
      "action": "assets.create",
      "params": {"name": "Gems", "type": "currency", "data": {"symbol": "GEM", "purchasable": true, "premium": true}}
    },
    {
      "id": "player_wallet",
      "action": "wallets.create",
      "params": {"name": "Player Wallet", "currency": "gold"}
    },
    {
      "id": "starter_bonus",
      "action": "wallets.deposit",
      "params": {
        "wallet_id": {"from": "player_wallet.result.wallet_id"},
        "amount": 500,
        "description": "Starter gold bonus"
      }
    },
    {
      "id": "daily_reward_logic",
      "action": "logic.create",
      "params": {
        "name": "Daily Login Reward",
        "triggers": [{"type": "command", "command": "game.daily_login"}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "add",
            "parameters": {"field1": "user.data.gold", "field2": 50, "result_field": "user.data.gold"}
          },
          {
            "processor": "field_operations_processor",
            "action": "add",
            "parameters": {"field1": "user.data.daily_streak", "field2": 1, "result_field": "user.data.daily_streak"}
          }
        ],
        "enabled": true
      }
    },
    {
      "id": "streak_bonus_logic",
      "action": "logic.create",
      "params": {
        "name": "Streak Bonus (7-day)",
        "triggers": [{"type": "command", "command": "game.daily_login"}],
        "conditions": [{"field": "user.data.daily_streak", "op": "equals", "value": 7}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "add",
            "parameters": {"field1": "user.data.gems", "field2": 10, "result_field": "user.data.gems"}
          },
          {
            "processor": "field_operations_processor",
            "action": "set",
            "parameters": {"field": "user.data.daily_streak", "value": 0}
          }
        ],
        "enabled": true
      }
    },
    {
      "id": "iap_gem_pack",
      "action": "assets.create",
      "params": {
        "name": "Gem Pack 100",
        "type": "iap_product",
        "data": {"gems_amount": 100, "price_usd": 0.99, "sku": "gems_100"}
      }
    },
    {
      "id": "seasonal_event_buff",
      "action": "buffs.create_buff",
      "params": {
        "name": "Double XP Weekend",
        "duration_days": 2,
        "effects": {"xp_multiplier": 2.0, "gold_multiplier": 1.5},
        "revert_on_expire": true
      }
    }
  ]
}

Purchasing gems (IAP flow):

{
  "steps": [
    {
      "id": "pay",
      "action": "payments.create",
      "params": {"amount": 0.99, "currency": "USD", "description": "Gem Pack 100", "payment_method": "card", "metadata": {"sku": "gems_100"}}
    },
    {
      "id": "deposit_gems",
      "action": "wallets.deposit",
      "params": {
        "wallet_id": "$user_gem_wallet_id",
        "amount": 100,
        "description": "Gem Pack 100 purchase"
      },
      "if": {"from": "pay.result.status", "op": "equals", "value": "pending"}
    }
  ]
}

Key synergies:

  • assets.create defines economy schema; wallets.deposit funds it
  • logic.create with conditions = streak/combo reward engine
  • payments.create + if + wallets.deposit = verified IAP flow
  • buffs.create_buff as event template = activate for all users with one apply_buff

4. Loyalty & Rewards Program

Problem: Track points, award badges, tier-based perks, point expiry, and redemption for any type of app.

{
  "steps": [
    {
      "id": "points_asset",
      "action": "assets.create",
      "params": {"name": "Loyalty Points", "type": "points", "data": {"symbol": "LP", "expiry_days": 365}}
    },
    {
      "id": "bronze_buff",
      "action": "buffs.create_buff",
      "params": {
        "name": "Bronze Member",
        "effects": {"tier": "bronze", "discount_pct": 5, "priority_support": false},
        "revert_on_expire": false
      }
    },
    {
      "id": "silver_buff",
      "action": "buffs.create_buff",
      "params": {
        "name": "Silver Member",
        "effects": {"tier": "silver", "discount_pct": 10, "priority_support": true},
        "revert_on_expire": false
      }
    },
    {
      "id": "gold_buff",
      "action": "buffs.create_buff",
      "params": {
        "name": "Gold Member",
        "effects": {"tier": "gold", "discount_pct": 20, "priority_support": true, "free_shipping": true},
        "revert_on_expire": false
      }
    },
    {
      "id": "earn_points_rule",
      "action": "logic.create",
      "params": {
        "name": "Earn points on purchase",
        "triggers": [{"type": "command", "command": "event.purchase_completed"}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "add",
            "parameters": {
              "field1": "user.data.loyalty_points",
              "field2": 10,
              "result_field": "user.data.loyalty_points"
            }
          }
        ],
        "enabled": true
      }
    },
    {
      "id": "tier_upgrade_rule",
      "action": "logic.create",
      "params": {
        "name": "Auto tier upgrade",
        "triggers": [{"type": "command", "command": "loyalty.promote_silver"}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "set",
            "parameters": {"field": "user.data.tier", "value": "silver"}
          }
        ],
        "enabled": true
      }
    },
    {
      "id": "expiry_scheduler",
      "action": "scheduler.create_task",
      "params": {
        "name": "Monthly points expiry check",
        "task_type": "cron",
        "cron_expression": "0 2 1 * *",
        "payload": {"action": "expire_loyalty_points", "expiry_days": 365}
      }
    }
  ]
}

Key synergies:

  • assets.create = points schema with metadata
  • buffs.create_buff x3 = tier templates, apply instantly to any user
  • logic.create matches type: command. Emit loyalty.promote_silver when the app decides the threshold. field2 is a number; the processor does not read from_event.
  • scheduler.create_task with cron = automatic expiry without extra code

5. E-commerce Checkout Flow

Problem: Read the inventory leaf, accept payment, credit loyalty points, and schedule a follow-up. Stock writes stay on projects.patch_data.

{
  "steps": [
    {
      "id": "stock",
      "action": "projects.get_data",
      "params": {
        "project_id": "$project_id",
        "path": "commerce.inventory"
      }
    },
    {
      "id": "payment",
      "action": "payments.create",
      "params": {
        "amount": "$order_total",
        "currency": "USD",
        "description": "Order #$order_id",
        "payment_method": "card",
        "metadata": {"order_id": "$order_id", "product_ids": "$product_ids"}
      },
      "if": {"from": "stock.result.value", "op": "exists"}
    },
    {
      "id": "add_loyalty_points",
      "action": "wallets.deposit",
      "params": {
        "wallet_id": "$user_loyalty_wallet_id",
        "amount": {"from": "payment.result.amount"},
        "description": "Loyalty points for order"
      },
      "if": {"from": "payment.result.status", "op": "equals", "value": "pending"}
    },
    {
      "id": "fulfillment_task",
      "action": "scheduler.create_task",
      "params": {
        "name": "Fulfill order $order_id",
        "task_type": "once",
        "run_at": "now",
        "payload": {"action": "trigger_fulfillment", "order_id": "$order_id", "payment_id": {"from": "payment.result.payment_id"}}
      },
      "if": {"from": "payment.result.status", "op": "equals", "value": "pending"}
    },
    {
      "id": "checkout_health",
      "action": "analytics.project_snapshot",
      "params": {"include": ["activity", "finance", "product_events"]}
    }
  ]
}

Key synergies:

  • projects.get_data on commerce.inventory is the stock read. op: exists continues only when that leaf is present. A stock change is projects.patch_data on the same path, not commands.execute.
  • wallets.deposit = loyalty points on every purchase (no extra service)
  • scheduler.create_task with run_at: now stores a one-time task. trigger_fulfillment is not a registered action, so the create response sets action_registered to false.
  • analytics.project_snapshot reads project health KPIs after checkout. It does not record a purchase.

6. AI Automation Pipeline

Problem: Build a self-maintaining pipeline: monitor usage, auto-scale limits, alert on thresholds, and run cleanup tasks.

{
  "steps": [
    {
      "id": "usage_monitor_rule",
      "action": "logic.create",
      "params": {
        "name": "API quota monitor",
        "triggers": [{"type": "command", "command": "system.api_call_completed"}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "add",
            "parameters": {"field1": "project.data.api_calls_today", "field2": 1, "result_field": "project.data.api_calls_today"}
          }
        ],
        "enabled": true
      }
    },
    {
      "id": "threshold_alert_rule",
      "action": "logic.create",
      "params": {
        "name": "Usage threshold alert",
        "triggers": [{"type": "command", "command": "usage.quota_warning"}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "set",
            "parameters": {"field": "project.data.quota_warning_sent", "value": true}
          }
        ],
        "enabled": true
      }
    },
    {
      "id": "auto_scale_buff",
      "action": "buffs.create_buff",
      "params": {
        "name": "Emergency quota boost",
        "duration_days": 1,
        "effects": {"api_calls_limit": 20000, "overage_mode": true},
        "revert_on_expire": true
      }
    },
    {
      "id": "daily_reset_task",
      "action": "scheduler.create_task",
      "params": {
        "name": "Daily API counter reset",
        "task_type": "cron",
        "cron_expression": "0 0 * * *",
        "payload": {
          "action": "reset_counter",
          "fields": ["project.data.api_calls_today", "project.data.quota_warning_sent"]
        }
      }
    },
    {
      "id": "weekly_analytics_task",
      "action": "scheduler.create_task",
      "params": {
        "name": "Weekly usage report",
        "task_type": "cron",
        "cron_expression": "0 9 * * 1",
        "payload": {"action": "generate_usage_report", "send_email": true}
      }
    },
    {
      "id": "stale_data_cleanup",
      "action": "scheduler.create_task",
      "params": {
        "name": "Stale session cleanup",
        "task_type": "cron",
        "cron_expression": "0 4 * * *",
        "payload": {"action": "storage.cleanup_temp"}
      }
    }
  ]
}

Key synergies:

  • logic.create counts on system.api_call_completed. The warning rule runs when something emits usage.quota_warning.
  • buffs.create_buff (emergency boost) ready to apply when threshold_alert_rule fires
  • Daily cleanup uses storage.cleanup_temp. reset_counter and generate_usage_report are not registered actions; create returns action_registered: false.
  • All steps reference project_id from context — no explicit passing needed

7. Content Platform with Access Control

Problem: Create a content library, gate premium content behind subscriptions, track consumption, and manage editorial permissions.

{
  "steps": [
    {
      "id": "free_tier_buff",
      "action": "buffs.create_buff",
      "params": {
        "name": "Free Access",
        "effects": {"content_tier": "free", "articles_per_month": 3, "downloads": 0},
        "revert_on_expire": false
      }
    },
    {
      "id": "premium_buff",
      "action": "buffs.create_buff",
      "params": {
        "name": "Premium Subscription",
        "duration_days": 30,
        "effects": {"content_tier": "premium", "articles_per_month": 999, "downloads": 50},
        "revert_on_expire": true,
        "on_expire": [{"command": "event.premium_expired"}]
      }
    },
    {
      "id": "content_asset_pdf",
      "action": "assets.create",
      "params": {
        "name": "Annual Report 2025",
        "type": "document",
        "data": {"tier": "premium", "format": "pdf", "file_size_mb": 4.2}
      }
    },
    {
      "id": "article_limit_rule",
      "action": "logic.create",
      "params": {
        "name": "Article consumption tracker",
        "triggers": [{"type": "command", "command": "content.article_read"}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "add",
            "parameters": {"field1": "user.data.articles_read_this_month", "field2": 1, "result_field": "user.data.articles_read_this_month"}
          }
        ],
        "enabled": true
      }
    },
    {
      "id": "fap_policy",
      "action": "data_access.set_policy",
      "params": {
        "resources": {
          "premium_content": {
            "default_access": "deny",
            "fields": {
              "body": {"read": "authenticated", "write": "deny", "condition": "{{user.role}} == 'admin'"},
              "preview": "authenticated"
            }
          }
        }
      }
    },
    {
      "id": "editor_role",
      "action": "rbac.assign_role",
      "params": {"user_id": "$editor_user_id", "role": "editor"}
    },
    {
      "id": "monthly_reset",
      "action": "scheduler.create_task",
      "params": {
        "name": "Reset monthly article counts",
        "task_type": "cron",
        "cron_expression": "0 0 1 * *",
        "payload": {"action": "reset_monthly_counters", "field": "user.data.articles_read_this_month"}
      }
    }
  ]
}

Key synergies:

  • buffs.create_buff (free/premium templates) + data_access.set_policy = content gating. Field descriptors are deny / authenticated / {read, write, condition}. A condition is a string the evaluator already runs ({{user.role}} == 'admin'). An object allow_if is stored as deny.
  • assets.create = content catalog with metadata
  • logic.create counter + scheduler.create_task reset = metered access model
  • rbac.assign_role = editorial permissions

8. Social Community Activation

Problem: Onboard a user into a social community: create profile, join a channel, send first welcome message, connect with community bot.

{
  "steps": [
    {
      "id": "publish_profile",
      "action": "auth.update_profile",
      "params": {
        "display_name": "$user_name",
        "bio": "$bio"
      }
    },
    {
      "id": "join_general",
      "action": "social.channel_invites.redeem",
      "params": {"token": "$general_channel_invite_token"}
    },
    {
      "id": "welcome_message",
      "action": "social.chat.post",
      "params": {
        "channel_id": "$general_channel_id",
        "text": "Hey everyone! I just joined."
      }
    },
    {
      "id": "friend_bot",
      "action": "social.friends.request",
      "params": {"target_user_id": "$community_bot_user_id"}
    },
    {
      "id": "index_profile",
      "action": "social.public.publish",
      "params": {
        "home_project_id": 1,
        "kind": "channels",
        "channel_id": "$general_channel_id"
      }
    },
    {
      "id": "onboarding_buff",
      "action": "buffs.apply_buff",
      "params": {
        "buff_id": "$onboarding_buff_template_id",
        "entity_id": "$user_id",
        "entity_kind": "user"
      }
    }
  ]
}

RAG-enhanced community search:

{
  "steps": [
    {
      "id": "index_message",
      "action": "rag.document_add",
      "params": {
        "collection_id": "$community_rag_collection_id",
        "text": "$message_text",
        "metadata": {"author": "$user_id", "channel": "$channel_id", "ts": "$timestamp"}
      }
    },
    {
      "id": "search",
      "action": "rag.search",
      "params": {
        "collection_id": "$community_rag_collection_id",
        "query": "how to get started with the API",
        "limit": 5
      }
    }
  ]
}

Key synergies:

  • auth.update_profile sets the display name. social.public.publish lists a channel, it does not store a user bio. Avatar upload is POST /api/profile/avatar.
  • social.channel_invites.redeem = join any channel with single-use token
  • buffs.apply_buff = onboarding perks applied automatically
  • rag.document_add on chat messages = searchable community knowledge base

9. Multi-tenant B2B Onboarding

Problem: Enterprise sale — provision a new tenant organization, set up their team, configure feature flags per contract tier, and hand off API credentials.

{
  "steps": [
    {
      "id": "org_project",
      "action": "projects.create_project",
      "params": {
        "name": "$company_name Workspace",
        "data": {
          "config": {
            "org_id": "$crm_org_id",
            "contract_tier": "enterprise",
            "seats": 50
          }
        }
      }
    },
    {
      "id": "admin_role",
      "action": "rbac.assign_role",
      "params": {
        "user_id": "$admin_user_id",
        "role": "admin",
        "project_id": {"from": "org_project.result.project_id"}
      }
    },
    {
      "id": "enterprise_buff",
      "action": "buffs.apply_buff",
      "params": {
        "buff_id": "$enterprise_tier_buff_id",
        "entity_id": {"from": "org_project.result.project_id"},
        "entity_kind": "project"
      }
    },
    {
      "id": "sso_key",
      "action": "apikeys.create",
      "params": {
        "name": "$company_name SSO Integration",
        "service_caps": ["analytics"],
        "project_id": {"from": "org_project.result.project_id"}
      }
    },
    {
      "id": "agent_key",
      "action": "apikeys.create",
      "params": {
        "name": "$company_name AI Agent",
        "service_caps": ["payments", "scheduler"],
        "project_id": {"from": "org_project.result.project_id"}
      }
    },
    {
      "id": "invite_team",
      "action": "social.channel_invites.direct",
      "params": {
        "home_project_id": {"from": "org_project.result.project_id"},
        "user_ids": "$team_user_ids",
        "channel_id": "$org_channel_id"
      }
    },
    {
      "id": "fap_defaults",
      "action": "data_access.apply_defaults_template",
      "params": {"project_id": {"from": "org_project.result.project_id"}}
    },
    {
      "id": "health_check_task",
      "action": "scheduler.create_task",
      "params": {
        "name": "Weekly tenant health check",
        "task_type": "cron",
        "cron_expression": "0 8 * * 1",
        "payload": {"action": "tenant_health_report", "project_id": {"from": "org_project.result.project_id"}}
      }
    }
  ]
}

Key synergies:

  • projects.create_project + rbac.assign_role = full org with owner in 2 steps
  • buffs.apply_buff on entity_kind: "project" = project-level enterprise features
  • apikeys.create x2 with different service_caps = SSO key (analytics only) + AI key (payments + scheduler)
  • data_access.apply_defaults_template = instant policy without manual setup

10. Event-driven Marketing Campaign

Problem: Run a time-limited campaign: bonus credits for purchases, double XP weekend, leaderboard tracking, and automatic campaign end.

{
  "steps": [
    {
      "id": "campaign_buff",
      "action": "buffs.create_buff",
      "params": {
        "name": "Summer Campaign 2025",
        "duration_days": 7,
        "effects": {
          "bonus_credits_pct": 20,
          "xp_multiplier": 2.0,
          "campaign_active": true
        },
        "revert_on_expire": true,
        "on_expire": [{"command": "event.campaign_ended", "payload": {"campaign_id": "summer_2025"}}]
      }
    },
    {
      "id": "apply_to_project",
      "action": "buffs.apply_buff",
      "params": {
        "buff_id": {"from": "campaign_buff.result.buff_id"},
        "entity_id": "$project_id",
        "entity_kind": "project"
      }
    },
    {
      "id": "bonus_credits_rule",
      "action": "logic.create",
      "params": {
        "name": "Campaign purchase bonus",
        "triggers": [{"type": "command", "command": "event.purchase_completed"}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "multiply",
            "parameters": {
              "field1": "event.purchase_amount",
              "field2": 0.2,
              "result_field": "user.data.bonus_credits"
            }
          }
        ],
        "enabled": true
      }
    },
    {
      "id": "leaderboard_rule",
      "action": "logic.create",
      "params": {
        "name": "Campaign leaderboard tracker",
        "triggers": [{"type": "command", "command": "event.xp_earned"}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "add",
            "parameters": {"field1": "user.data.campaign_score", "field2": 1, "result_field": "user.data.campaign_score"}
          }
        ],
        "enabled": true
      }
    },
    {
      "id": "leaderboard_asset",
      "action": "assets.create",
      "params": {
        "name": "Campaign Leaderboard Trophy",
        "type": "achievement",
        "data": {"campaign": "summer_2025", "rank_required": 1}
      }
    },
    {
      "id": "campaign_end_task",
      "action": "scheduler.create_task",
      "params": {
        "name": "Campaign end: award trophies",
        "task_type": "once",
        "run_at": "$campaign_end_datetime",
        "payload": {"action": "award_campaign_trophies", "asset_id": {"from": "leaderboard_asset.result.asset_id"}}
      }
    }
  ]
}

Key synergies:

  • buffs.apply_buff on project = global state flag readable by all logic rules
  • logic.create fires on the command name. A top-level conditions array is not read by the matcher.
  • assets.create (trophy) + scheduler.create_task (award) = automated prize distribution

11. Usage-based Billing with Auto-throttling

Problem: Measure API usage, bill on consumption, auto-downgrade on non-payment, auto-upgrade limits when usage grows.

{
  "steps": [
    {
      "id": "usage_counter_logic",
      "action": "logic.create",
      "params": {
        "name": "Increment monthly API counter",
        "triggers": [{"type": "command", "command": "system.api_call_authenticated"}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "add",
            "parameters": {"field1": "user.data.api_calls_this_month", "field2": 1, "result_field": "user.data.api_calls_this_month"}
          }
        ],
        "enabled": true
      }
    },
    {
      "id": "overage_detect_logic",
      "action": "logic.create",
      "params": {
        "name": "Detect quota overage",
        "triggers": [{"type": "command", "command": "system.api_call_authenticated"}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "set",
            "parameters": {"field": "user.data.quota_exceeded", "value": true}
          }
        ],
        "enabled": true
      }
    },
    {
      "id": "monthly_invoice_task",
      "action": "scheduler.create_task",
      "params": {
        "name": "Monthly usage billing",
        "task_type": "cron",
        "cron_expression": "0 10 1 * *",
        "payload": {
          "action": "generate_and_charge_invoice",
          "metric_field": "user.data.api_calls_this_month",
          "rate_per_call": 0.001
        }
      }
    },
    {
      "id": "throttle_buff",
      "action": "buffs.create_buff",
      "params": {
        "name": "Quota Exceeded — Throttled",
        "effects": {"rate_limit_rps": 1, "quota_exceeded": true},
        "revert_on_expire": false,
        "description": "Applied when user exceeds plan quota without upgrade"
      }
    },
    {
      "id": "reset_counter_task",
      "action": "scheduler.create_task",
      "params": {
        "name": "Monthly counter reset",
        "task_type": "cron",
        "cron_expression": "0 0 1 * *",
        "payload": {
          "action": "reset_field",
          "fields": ["user.data.api_calls_this_month", "user.data.quota_exceeded"]
        }
      }
    }
  ]
}

Key synergies:

  • logic.create matches type: command only. There is no field_change trigger.
  • buffs.create_buff (throttle template) = instant apply when overage detected
  • scheduler.create_task x2 = billing + reset in one setup call

12. Player Matchmaking & Seasonal Events

Problem: Create a matchmaking system with ELO ratings, seasonal ranking resets, and tiered seasonal rewards.

{
  "steps": [
    {
      "id": "season_buff",
      "action": "buffs.create_buff",
      "params": {
        "name": "Season 1",
        "duration_days": 90,
        "effects": {"season": 1, "season_active": true, "elo_visible": true},
        "revert_on_expire": true,
        "on_expire": [{"command": "event.season_ended", "payload": {"season": 1}}]
      }
    },
    {
      "id": "apply_season",
      "action": "buffs.apply_buff",
      "params": {
        "buff_id": {"from": "season_buff.result.buff_id"},
        "entity_id": "$project_id",
        "entity_kind": "project"
      }
    },
    {
      "id": "elo_update_rule",
      "action": "logic.create",
      "params": {
        "name": "ELO update after match",
        "triggers": [{"type": "command", "command": "game.match_completed"}],
        "space": [
          {
            "processor": "field_operations_processor",
            "action": "add",
            "parameters": {"field1": "user.data.elo", "field2": 15, "result_field": "user.data.elo"}
          },
          {
            "processor": "field_operations_processor",
            "action": "add",
            "parameters": {"field1": "user.data.season_points", "field2": 10, "result_field": "user.data.season_points"}
          }
        ],
        "enabled": true
      }
    },
    {
      "id": "gold_rank_reward",
      "action": "assets.create",
      "params": {
        "name": "Season 1 Gold Badge",
        "type": "badge",
        "data": {"season": 1, "rank": "gold", "elo_threshold": 1800}
      }
    },
    {
      "id": "season_end_task",
      "action": "scheduler.create_task",
      "params": {
        "name": "Season 1 end: distribute rewards + reset",
        "task_type": "once",
        "run_at": "$season_end_datetime",
        "payload": {
          "action": "season_end_processing",
          "rank_asset_id": {"from": "gold_rank_reward.result.asset_id"},
          "reset_field": "user.data.season_points"
        }
      }
    },
    {
      "id": "matchmaking_rag_collection",
      "action": "rag.collection_create",
      "params": {
        "name": "matchmaking_player_profiles",
        "description": "Player skill profiles for semantic matchmaking"
      }
    }
  ]
}

Key synergies:

  • buffs.apply_buff on project = global season state, readable by all rules
  • logic.create = ELO updates without server code
  • assets.create (badge) + scheduler.create_task = automatic end-of-season awards
  • rag.collection_create = semantic player matching (add player profiles, search by skill tags)

9. Project support (user → team)

Problem: A signed-in user needs to message a project's support team without joining a public channel.

Flow: Discover staff inbox capability via roles, read thread, send message (MCP mirrors REST).

{
  "steps": [
    {
      "id": "roles",
      "action": "social.support.inbox",
      "params": {"project_id": "$home_project_id", "limit": 5}
    },
    {
      "id": "history",
      "action": "social.support.history",
      "params": {"project_id": "$home_project_id", "limit": 50}
    },
    {
      "id": "send",
      "action": "social.support.send",
      "params": {"project_id": "$home_project_id", "body": "I need help with billing."}
    }
  ]
}

Staff-only listing requires API key cap project_support on social.support.inbox; send/history use session RBAC inside the tool.


Cross-flow patterns

Pattern A — Payment-gated feature activation

Tenant paid SKU: commerce.sell.activate → payments.create → webhook/C24 auto-grant
Tenant manual: buffs.grant_tenant_subscription
Platform operator: buffs.grant_subscription (context.project_id=1)
IAP / loyalty only: payments.create → (if completed) → buffs.apply_persistent_effect

Pattern B — Reactive logic chain

logic.create (trigger: command) → field update → another command rule

Pattern C — Time-boxed campaign

buffs.create_buff (revert_on_expire) → buffs.apply_buff (entity_kind: project) → logic.create (reads buff effect)

Pattern D — Scheduled economy

scheduler.create_task (cron) → wallets.deposit OR payments.create (tenant subscription: webhook grant) OR buffs.apply_temporary_effect (non-subscription perks)

Pattern E — Safe agent provisioning

apikeys.create (service_caps: ["payments"]) → use returned api_key for payment-only agent

Pattern F — Content metering

logic.create (counter) → scheduler.create_task (monthly reset) + buffs.create_buff (limit template)

Pattern G — Safe sandbox mutation

generation.fork → projects.patch_data (sandbox only) → generation.diff → generation.gates → generation.promote

Pattern H — Business organism bootstrap

business.create_composite → business.get_org → business.apply_tariff (optional) → business.command_snapshot

Platform comparison table

Axis Sandbox generation (generation.*) Business organism (business.*)
Goal Experiment with project config/DNA before production Provision multi-project org (head + organ children)
Entry generation.fork on an existing project business.create_composite greenfield
Promote / commit generation.promote, generation.canary.advance business.apply_tariff, attach/detach organs — not generation promote
Read model generation.status, generation.diff, generation.timeline business.get_org, business.command_snapshot
Wrong tool Composite create for a one-off config tweak generation.fork when you need CRM + storefront organs
Axis hosting.release.promote generation.promote
Plane Static hosting — immutable site bucket snapshots 8DNA — sandbox environment production pointer
Typical pre-step commerce.storefront.hosted_publish or hosting.release.snapshot generation.fork → mutate → generation.gates
Audit hosting.release.list generation.timeline
Bridge generation.hosting.release labels a snapshot with sandbox gen (preview; swap URL separately) Hosted vitrine publish is separate after generation promote

13. Marketplace storefront buy (REST + wallet)

Problem: Agent discovers a listing globally and completes purchase with internal USDT wallet.

REST chain (not MCP step.action for listing lifecycle):

  1. GET /api/commerce/storefront/listings?scope=global&q=sword
  2. POST /api/commerce/checkout/sessions + header Idempotency-Key
  3. POST /api/commerce/checkout/sessions/{id}/confirm
{
  "listing_uuid": "$listing_uuid",
  "rail": "wallet_internal"
}

Merchant bulk (project-scoped):

  1. assets.create (MCP) for catalog rows
  2. POST /api/commerce/marketplace/listings/bulk with X-Project-ID

SDK:

await sdk.commerce.listStorefront({ scope: 'global' });
await sdk.commerce.checkout.createSession({ listing_uuid, rail: 'wallet_internal' });

Reference

  • Full action catalog: GET /mcp/actions
  • Current key capabilities: GET /mcp/discovery → api_key_context.service_caps
  • Project setup playbooks: docs/MCP_PROJECT_SETUP_PLAYBOOKS.md
  • Commerce flows: docs/MCP_ECONOMY_AND_COMMERCE_MAP.md
  • API key safety: docs/API_KEY_SERVICE_CAPS.md

13. Read-only session bootstrap

Problem: A 6-step batch with stopOnError dies on buffs.get_effective_limits or apikeys.list (service_cap_denied) and never reaches quota.

Tool chain: auth.get_profile → projects.get_stats → buffs.get_effective_limits → storage.get_quota with continueOnError: true. Omit apikeys.list unless the key has L1 api_keys. Omit entity_kind (string "user"/"project" only if passed).

Recipe: mcp_read_bootstrap sets continueOnError unless the request passes stopOnError. The profile step asks for identity only (include_settings: false). Prompt: agentstack_read_bootstrap. Default stopOnError for mutation batches stays true.

{
  "steps": [],
  "options": {"recipe_id": "mcp_read_bootstrap"}
}

14. Seller one-click

Problem: A creator wants to sell digital goods in one session — seed catalog, bind earnings wallet, publish a hosted vitrine, and verify release history.

Tool chain: commerce.sell.activate → commerce.storefront.hosted_publish → hosting.release.list

{
  "steps": [
    {
      "id": "activate",
      "action": "commerce.sell.activate",
      "params": {
        "project_id": "$project_id",
        "user_id": "$user_id",
        "use_starter": true,
        "publish_hosted": false,
        "storefront_public": true,
        "idempotency_key": "seller-one-click-v1"
      }
    },
    {
      "id": "publish",
      "action": "commerce.storefront.hosted_publish",
      "params": {
        "project_id": "$project_id",
        "user_id": "$user_id",
        "bucket_name": "$store_slug"
      },
      "if": {"from": "activate.result.success", "op": "equals", "value": true}
    },
    {
      "id": "releases",
      "action": "hosting.release.list",
      "params": {
        "project_id": "$project_id",
        "bucket_id": "$store_slug",
        "limit": 10
      },
      "if": {"from": "publish.result.success", "op": "equals", "value": true}
    }
  ]
}

REST mirror: POST /api/commerce/sell/activate · POST /api/commerce/storefront/hosted/publish

Key synergies:

  • commerce.sell.activate = earnings wallet + seed + storefront index in one orchestrated path
  • publish_hosted: false in activate + separate hosted_publish = agent can inspect seed before vitrine
  • hosting.release.list confirms immutable release rows after publish (newest first)
  • Activation share_kit returns hosted_url, shop_url, and embed hints for operator handoff

15. Generation safe mutation + canary promote

Problem: Change project config in a sandbox, review diff and gates, then promote to production with optional canary traffic steps.

Tool chain: generation.list → generation.fork → projects.patch_data → generation.diff → generation.gates → generation.promote → generation.canary.advance

Phase A — Fork and mutate

{
  "steps": [
    {
      "id": "envs",
      "action": "generation.list",
      "params": {"project_id": "$project_id"}
    },
    {
      "id": "fork",
      "action": "generation.fork",
      "params": {
        "project_id": "$project_id",
        "source_project_id": "$project_id",
        "env_name": "pricing-tweak",
        "env_type": "sandbox"
      }
    },
    {
      "id": "mutate",
      "action": "projects.patch_data",
      "params": {
        "project_id": "$project_id",
        "env_uuid": {"from": "fork.result.env_uuid"},
        "path": "config.pricing.pro_monthly_usd",
        "value": 39
      }
    }
  ]
}

Phase B — Gate, promote, advance canary

{
  "steps": [
    {
      "id": "diff",
      "action": "generation.diff",
      "params": {
        "project_id": "$project_id",
        "env_uuid_a": "$prod_env_uuid",
        "env_uuid_b": {"from": "fork.result.anchor_uuid"}
      }
    },
    {
      "id": "gates",
      "action": "generation.gates",
      "params": {
        "project_id": "$project_id",
        "env_uuid": {"from": "fork.result.anchor_uuid"}
      }
    },
    {
      "id": "promote",
      "action": "generation.promote",
      "params": {
        "project_id": "$project_id",
        "env_uuid": {"from": "fork.result.anchor_uuid"},
        "strategy": "immediate"
      },
      "if": {"from": "gates.result.all_passed", "op": "equals", "value": true}
    },
    {
      "id": "canary_step",
      "action": "generation.canary.advance",
      "params": {
        "project_id": "$project_id",
        "env_uuid": {"from": "fork.result.anchor_uuid"}
      },
      "if": {"from": "promote.result.status", "op": "equals", "value": "rolling_out"}
    }
  ]
}

Key synergies:

  • generation.fork isolates mutations — production pointer unchanged until promote
  • generation.diff + generation.gates = review-before-promote without custom CI
  • generation.promote with strategy: "immediate" vs canary rolling_out → generation.canary.advance
  • Manual gate pending → generation.approve then re-run generation.gates
  • Rollback path → generation.canary.abort + generation.timeline audit

Not hosting.release.promote — that plane moves static site snapshots, not 8DNA sandboxes.


16. Business composite bootstrap

Problem: Greenfield business with organ children (CRM, storefront, bots, support), optional tariff, and a command-center read model in one batch.

Tool chain: business.create_composite → business.get_org → business.apply_tariff → business.command_snapshot

{
  "steps": [
    {
      "id": "composite",
      "action": "business.create_composite",
      "params": {
        "name": "Acme Studio",
        "organs": ["crm", "storefront", "bots", "support"],
        "tariff_template_id": "starter"
      }
    },
    {
      "id": "org",
      "action": "business.get_org",
      "params": {
        "project_id": {"from": "composite.result.head_project_id"}
      }
    },
    {
      "id": "tariff",
      "action": "business.apply_tariff",
      "params": {
        "project_id": {"from": "composite.result.head_project_id"},
        "template_id": "growth"
      },
      "if": {"from": "composite.result.success", "op": "equals", "value": true}
    },
    {
      "id": "snapshot",
      "action": "business.command_snapshot",
      "params": {
        "project_id": {"from": "composite.result.head_project_id"},
        "include_integrations": true
      }
    }
  ]
}

REST mirror: POST /api/business/composite

Key synergies:

  • business.create_composite = head project + organ child projects + org index in one saga
  • business.get_org confirms config.org children before downstream organ-specific MCP
  • business.command_snapshot = treasury + CRM summary + integration health (read-only aggregate)
  • Link existing standalone projects with business.link_child instead of create_composite when adopting

Not generation.fork — business organism is multi-project topology, not sandbox DNA.


17. Services inquiry + downstream MCP

Problem: A guest requests a professional-services quote on /services/:sku. The public BFF is REST-only; staff follow up via CRM MCP on ecosystem project 1.

REST chain (guest — not agentstack.execute):

  1. GET /api/public/services/catalog
  2. GET /api/public/services/skus/{sku_id}
  3. POST /api/public/services/inquiry → 202 Accepted
{
  "sku": "implementation-sprint",
  "name": "Jane Doe",
  "email": "jane@example.com",
  "company": "Example Corp",
  "message": "We need AgentStack CRM + storefront for 50 seats.",
  "locale": "en"
}

Downstream MCP (staff on ecosystem pid=1):

{
  "steps": [
    {
      "id": "find_lead",
      "action": "crm.search",
      "params": {
        "project_id": 1,
        "q": "jane@example.com",
        "limit": 5
      }
    },
    {
      "id": "contact_360",
      "action": "crm.get_contact_360",
      "params": {
        "project_id": 1,
        "contact_id": {"from": "find_lead.result.contacts[0].id"}
      }
    },
    {
      "id": "log_followup",
      "action": "crm.log_activity",
      "params": {
        "project_id": 1,
        "contact_id": {"from": "find_lead.result.contacts[0].id"},
        "kind": "note",
        "body": "Staff replied to services inquiry — scheduling discovery call."
      }
    },
    {
      "id": "deal",
      "action": "crm.create_deal",
      "params": {
        "project_id": 1,
        "contact_id": {"from": "find_lead.result.contacts[0].id"},
        "title": "Implementation sprint — Example Corp",
        "stage_id": "qualified"
      }
    }
  ]
}

Key synergies:

  • Inquiry upserts CRM contact + timeline note on ecosystem project 1 (not tenant scope)
  • In-app notifications fan to pid=1 owner/admin with action_href → /dev/projects/1/crm
  • Honeypot field website silently accepts spam without persisting
  • Rate limit: 10 inquiries / hour / IP on POST /inquiry
  • No MCP action for guest submit — use REST BFF; staff use crm.* after acceptance

Gene: frontend.public.services_offers.gen1 · Hub: /services (not /pricing SaaS tiers)


18. Platform grant + tenant subscription monetization

Problem: Ecosystem operator grants AgentStack tiers (two planes) OR a tenant project sells subscription SKUs to end users.

Two rails (do not conflate)

Rail Who Buff target Entry
Platform operator Lance / ecosystem op user premium/vip OR project launch/business/scale buffs.grant_subscription or prompt agentstack_platform_subscription_grant
Tenant monetization Tenant sells to buyer buyer entity_kind=user on tenant PID prompt agentstack_tenant_subscription_monetization
Tenant manual grant Tenant operator (support/comp) buyer entity_kind=user on tenant PID buffs.grant_tenant_subscription · prompt agentstack_tenant_buff_grant

Platform operator — one-shot grant

{
  "context": { "project_id": 1 },
  "steps": [
    {
      "action": "buffs.grant_subscription",
      "params": { "plane": "personal", "tier": "vip", "entity_id": 1170, "verify": true }
    }
  ]
}

Project Scale example:

{
  "action": "buffs.grant_subscription",
  "params": { "plane": "project", "tier": "scale", "entity_id": 1444 }
}

Energy side-effect: Personal premium/vip also tops up AI energy via shared/subscription/personal_energy_grant.py on buffs.apply_buff (not limits-only).

SoT: shared/mcp/platform_subscription_grant.py · resolve_platform_grant_buff_params

Tenant monetization ladder

  1. buffs.create_buff — tenant SKU template (before sell.activate)
  2. commerce.sell.activate — subscription_entitlement + preset_inputs.buff_template_id
  3. payments.create — intent=subscription, metadata.recipient_project_id = tenant PID
  4. Verify — buffs.list_active_buffs on buyer

Post-purchase:

  • Card: webhook → grant_tenant_plan_buff (shared/mcp/tenant_buff_grant.py)
  • Shop: commerce.order.completed → C24 inline hook in commerce_events.py

Tenant manual entitlement (support / comp — no checkout)

SKU type MCP action SDK
Subscription buff buffs.grant_tenant_subscription grantTenantSubscription() from @agentstack/sdk/commerce/subscription
Digital goods inventory commerce.participant.grant_holding grantParticipantHolding() from @agentstack/sdk/commerce/participant
{
  "context": { "project_id": 1472 },
  "steps": [
    {
      "action": "buffs.grant_tenant_subscription",
      "params": {
        "user_id": 12345,
        "plan_id": "pro_monthly",
        "project_id": 1472,
        "verify": true
      }
    }
  ]
}

Recipe: mcp_tenant_manual_entitlement_grant · Idempotency: webhook uses payment:{payment_id}; shop C24 uses order:{order_id}:{buff_template_id} (shared/mcp/tenant_grant_idempotency.py).

Genes: shared.subscription.two_plane.gen1 · core.commerce.seller_activation.gen1 · core.commerce.assets.presets.gen1