← Zuora Coding AgentCONTENT HISTORY

Update to Zuora Coding Agent

Snapshot Sep 30, 2026 · 23:14 UTC · version 1.5.4

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Execute a Commerce Catalog dynamic pricing setup — create custom fields, attributes, products, plans, charges, and rate cards on the tenant",
  "included_files": [],
  "name": "zuora-dynamic-pricing-build",
  "skill_md_contents": "---\nname: zuora-dynamic-pricing-build\ndescription: Execute a Commerce Catalog dynamic pricing setup — create custom fields, attributes, products, plans, charges, and rate cards on the tenant\nargument-hint: [design reference or direct instructions]\nallowed-tools: [Read, Write, Edit, Glob, Grep, Bash, Agent, mcp__zuora-mcp__manage_commerce_products, mcp__zuora-mcp__manage_commerce_plans, mcp__zuora-mcp__manage_commerce_charges, mcp__zuora-mcp__manage_commerce_context_attributes, mcp__zuora-mcp__manage_custom_fields, mcp__zuora-mcp__query_objects, mcp__zuora-mcp__ask_zuora]\n---\n\nYou are executing a Commerce Catalog dynamic pricing setup directly on the user's Zuora tenant. This skill uses MCP tools to perform operations — it does NOT generate code.\n\n## Preflight: Check DynamicPricing is enabled\n\nBefore proceeding, call `manage_commerce_charges` with `{\"help\": true, \"operation\": \"create_charge\"}`. If the tool is not available (not listed / not found), stop and tell the user:\n\n> The DynamicPricing feature is not enabled on this tenant. Enable it at **Settings > Billing > Manage Features > Commerce**, then retry.\n\n## Input\n\nThe user's request: $ARGUMENTS\n\nThis could be:\n- A reference to a design from `/zuora-dynamic-pricing-design`\n- Direct instructions to create/update catalog entities\n- A request to update rate card prices or add new rows/attribute values\n\nIf the input is a raw business requirement (not a confirmed design artifact), **stop and ask the user to run `/zuora-dynamic-pricing-design` first** so the design can be reviewed and approved before any catalog mutations occur. Only proceed directly when the user supplies a previously approved design or gives explicit, self-contained build instructions.\n\n## Tool routing\n\n- `manage_commerce_context_attributes` — create/update context attributes\n- `manage_commerce_products` — create/update/delete products (can include plans and charges in one call)\n- `manage_commerce_plans` — create/update/delete plans\n- `manage_commerce_charges` — create/update/delete/query charges, update tier prices. Use `help: true` for per-operation guidance before calling.\n- `manage_custom_fields` — list/add/update custom field definitions on standard Zuora objects\n- `query_objects` — look up existing entities, tier IDs, custom field definitions\n- `mcp__zuora-mcp__ask_zuora` — only as a fallback for unresolved product-behavior questions after checking charge help first\n\n## Workflow\n\n### Step 0: Ensure mapped fields exist\n\nAttributes can map to **standard fields** or **custom fields** (`__c` suffix). Standard fields already exist — only custom fields need creation.\n\nTo discover available fields on an object, call `query_objects` with `help: \"fields\"` and the target `objectType`.\n\nAttributes can map to fields on multiple objects:\n- `Account` — standard or custom fields\n- `Account.BillToContact` — fields from the bill-to contact on the account\n- `Account.SoldToContact` — fields from the sold-to contact on the account\n- `Subscription` — standard or custom fields\n- `RatePlan` — standard or custom fields\n- `Usage` — usage record fields (preferred for usage charges)\n\n**Mapping level:** If the design doesn't specify the mapping object for an attribute, confirm with the user before proceeding. Choose based on at what granularity the value changes:\n- `account` — fixed per customer, shared across all subscriptions\n- `subscription` — can differ between subscriptions for the same customer\n- `rate_plan` — set per rate plan instance within a subscription\n- `usage` — varies per event/record (only available on usage charges)\n\n**If custom fields are needed**, use `manage_custom_fields` to add them. First call `list_custom_fields` with the target `objectType` to check what exists, then `add_custom_field` for each missing field:\n- `objectType` — e.g., `Account`, `Subscription`, `RatePlan`\n- `fieldName` — must end with `__c` (auto-appended if omitted)\n- `label` — display name in Zuora UI\n- `fieldType` — `string` or `integer` (default: `string`)\n\nConfirm each custom field was created successfully before proceeding.\n\n### Step 1: Setup context attributes\n\nCreate or update context attributes that drive dynamic pricing.\n\n**List existing:**\n```\nTool: manage_commerce_context_attributes\noperation: list_attributes\n```\n\n**Create new attribute (custom field mapping):**\n```\nTool: manage_commerce_context_attributes\noperation: create_attribute\ncontextSchemaId: \"location\"\nattributeJson: {\n  \"slug\": \"region\",\n  \"name\": \"Region\",\n  \"type\": \"STRING\",\n  \"mapping\": [{\n    \"slug\": \"zuora-region\",\n    \"sourceSystem\": \"zuora\",\n    \"path\": \"Account.Region__c\",\n    \"validValues\": {\"stringValues\": [\"US\", \"EU\", \"APAC\"]}\n  }]\n}\n```\n\n**Create attribute mapped to standard field:**\n\nFirst use `query_objects` with `help: \"fields\"` and `objectType: \"Account\"` to confirm the standard field name, then:\n```\nTool: manage_commerce_context_attributes\noperation: create_attribute\ncontextSchemaId: \"customer_context\"\nattributeJson: {\n  \"slug\": \"currency\",\n  \"name\": \"Currency\",\n  \"type\": \"STRING\",\n  \"mapping\": [{\n    \"slug\": \"zuora-currency\",\n    \"sourceSystem\": \"zuora\",\n    \"path\": \"Account.Currency\",\n    \"validValues\": {\"stringValues\": [\"USD\", \"EUR\", \"GBP\"]}\n  }]\n}\n```\n\n**Add new values to existing attribute:**\n```\nTool: manage_commerce_context_attributes\noperation: update_attribute_values\ncontextSchemaId: \"location\"\nattributeSlug: \"region\"\n... (add new valid values)\n```\n\n### Step 2: Create product\n\nTwo approaches available:\n\n**Option A — Product only (then plan and charge separately):**\n```\nTool: manage_commerce_products\noperation: create_product\nproductJson: {\n  \"name\": \"Product Name\",\n  \"description\": \"...\",\n  \"startDate\": \"2026-01-01\",\n  \"endDate\": \"2099-12-31\",\n  \"category\": \"base\"\n}\n```\n\n**Option B — Full hierarchy in one call (product + plans + charges):**\n```\nTool: manage_commerce_products\noperation: create_product\nproductJson: {\n  \"name\": \"Product Name\",\n  \"startDate\": \"2026-01-01\",\n  \"endDate\": \"2099-12-31\",\n  \"category\": \"base\",\n  \"plans\": [{\n    \"name\": \"Standard Plan\",\n    \"startDate\": \"2026-01-01\",\n    \"endDate\": \"2099-12-31\",\n    \"activeCurrencies\": [\"USD\"],\n    \"charges\": [{\n      \"name\": \"Per-Seat Charge\",\n      \"chargeType\": \"recurring\",\n      \"chargeModel\": \"per_unit\",\n      \"unitOfMeasure\": \"seat\",\n      \"billCycle\": {\n        \"type\": \"default_from_customer\",\n        \"period\": \"bill_cycle_period_month\",\n        \"periodAlignment\": \"align_to_charge\",\n        \"timing\": \"in_advance\"\n      },\n      \"triggerEvent\": \"contract_effective\",\n      \"endDateCondition\": \"subscription_end\",\n      \"pricing\": {\"unitAmounts\": {\"USD\": 10.00}}\n    }]\n  }]\n}\n```\n\nSave the returned product `id` and plan `id` for subsequent operations.\n\n**Product grouping:** Pricing variations (on-demand vs reserved, US vs EU) should be rate cards on a SINGLE charge — NOT separate products. Create one product, one plan, one charge with multiple rate cards.\n\n### Step 3: Create plan (if using Option A)\n\n```\nTool: manage_commerce_plans\noperation: create_plan\nplanJson: {\n  \"productKey\": \"<product-id>\",\n  \"name\": \"Plan Name\",\n  \"startDate\": \"2026-01-01\",\n  \"endDate\": \"2099-12-31\",\n  \"activeCurrencies\": [\"USD\"]\n}\n```\n\n**Constraint:** Plan `startDate` must be >= parent product's `startDate`.\n\n### Step 4: Create charge with dynamic pricing\n\nBefore creating a charge, call `manage_commerce_charges` with `help: true` to get field guidance for the chosen operation.\n\n**Required fields for all charges:**\n- `charge.name` — display name\n- `charge.chargeType` — `recurring`, `one_time`, or `usage`\n- `charge.chargeModel` — pricing model enum\n- `charge.productRatePlanId` — plan ID from step 2/3\n- `charge.billCycle.type` — e.g., `default_from_customer`\n- `charge.billCycle.period` — e.g., `bill_cycle_period_month`\n- `charge.billCycle.periodAlignment` — `align_to_charge`\n- `charge.billCycle.timing` — `in_advance` or `in_arrears` (**OMIT for usage charges**)\n- `charge.triggerEvent` — `contract_effective`, `service_activation`, or `customer_acceptance`\n- `charge.endDateCondition` — `subscription_end`\n\n**Model-specific required fields:**\n\n| Model | Additional required |\n|-------|---------------------|\n| flat_fee | `pricing.flatAmounts` (map: `{\"USD\": 99.99}`) |\n| per_unit | `pricing.unitAmounts`, `unitOfMeasure`, `defaultQuantity` |\n| tiered | `pricing.tiers`, `pricing.tierMode`, `unitOfMeasure` |\n| volume | `pricing.tiers`, `pricing.tierMode`, `unitOfMeasure` |\n| tiered_overage | `pricing.tiers` (ALL need `upTo`), `pricing.unitAmounts`, `unitOfMeasure` |\n| overage | `pricing.unitAmounts`, `unitOfMeasure` |\n| discount_percentage | `pricing.discountPercentages`, `discountOptions` |\n| discount_fixed_amount | `pricing.discountAmounts`, `discountOptions` |\n\n**Adding dynamic pricing (attributes + rate cards):**\n\n```\nTool: manage_commerce_charges\noperation: create_charge\nchargeJson: {\n  \"charge\": {\n    \"name\": \"Dynamic Per-Unit Charge\",\n    \"chargeType\": \"recurring\",\n    \"chargeModel\": \"per_unit\",\n    \"productRatePlanId\": \"<plan-id>\",\n    \"billCycle\": {\n      \"type\": \"default_from_customer\",\n      \"period\": \"bill_cycle_period_month\",\n      \"periodAlignment\": \"align_to_charge\",\n      \"timing\": \"in_advance\"\n    },\n    \"triggerEvent\": \"contract_effective\",\n    \"endDateCondition\": \"subscription_end\",\n    \"unitOfMeasure\": \"seat\",\n    \"defaultQuantity\": 1,\n    \"pricing\": {\"unitAmounts\": {\"USD\": 10.00}},\n    \"attributes\": [\n      {\n        \"name\": \"Region\",\n        \"type\": \"String\",\n        \"mapping\": {\"object\": \"account\", \"field\": \"Region__c\"}\n      }\n    ],\n    \"rateCards\": [\n      {\n        \"attributes\": [\n          {\"name\": \"Region\", \"operator\": \"==\", \"value\": {\"stringValue\": \"US\"}}\n        ],\n        \"pricing\": {\"unitAmounts\": {\"USD\": 10.00}}\n      },\n      {\n        \"attributes\": [\n          {\"name\": \"Region\", \"operator\": \"==\", \"value\": {\"stringValue\": \"EU\"}}\n        ],\n        \"pricing\": {\"unitAmounts\": {\"USD\": 8.00}}\n      }\n    ]\n  }\n}\n```\n\n**Attribute declaration:**\n- `name` — attribute display name\n- `type` — `String`, `Integer`, `Double` (NOT Decimal), `Boolean`, `Date`, `Datetime`\n- `mapping.object` — `account`, `account.billtocontact`, `account.soldtocontact`, `subscription`, `rate_plan`, or `usage` (choose by granularity: account = per-customer, subscription = per-sub, rate_plan = per-plan instance, usage = per-event)\n- `mapping.field` — field name on that object: standard (e.g., `Country`, `WorkEmail`) or custom (e.g., `Region__c`)\n- For contact sub-objects, the `path` in the attribute mapping JSON uses dot notation: `Account.BillToContact.FieldName` or `Account.SoldToContact.FieldName`\n\n**Default pricing:** The `pricing` field at charge level is the fallback when no rate card matches. Always include it.\n\n**Rate card value wrappers (CRITICAL):**\n- String: `{\"stringValue\": \"us\"}`\n- Integer: `{\"intValue\": 42}`\n- Double: `{\"numberValue\": 9.99}`\n- Boolean: `{\"boolValue\": true}`\n- Datetime (used for `EffectiveDate`): `{\"stringValue\": \"2026-04-01T00:00:00Z\"}` — ISO-8601 with a zone offset\n\n**Supported operators:** `==`, `>`, `>=`, `<`, `<=`, `between`, `between-inclusive`, `matches` (regex). NOT supported: `!=`.\n- For `between`/`between-inclusive`, value is an array: `[lower, upper]`\n- First matching rate card wins\n- `EffectiveDate` is a reserved attribute and accepts ONLY the `>=` operator (see the effective dating section below)\n\n### Effective dating (time-varying pricing)\n\nRate cards support effective dating so a price can change on a future date while the prior price is preserved as history. This is driven by a **reserved attribute named `EffectiveDate`**:\n\n- Do NOT declare `EffectiveDate` in the charge-level `attributes[]` array, and do NOT create a custom field or `mapping` for it. It is a reserved attribute the system understands — you only reference it inside a rate card's `attributes[]` criteria.\n- On a rate card row, `EffectiveDate` uses operator `>=` only (marks the row's effective-from instant). Any other operator is rejected.\n- The value is a `Datetime` wrapped as `{\"stringValue\": \"...\"}` in ISO-8601 with a zone offset, e.g. `2026-04-01T00:00:00Z` or `2026-04-01T00:00:00-08:00`. A bare local datetime (no offset) is rejected.\n- If a rate card row omits `EffectiveDate`, it is treated as effective from now.\n\n**Set an initial effective-from date on a rate card row:**\n```\n{\n  \"attributes\": [\n    {\"name\": \"Region\", \"operator\": \"==\", \"value\": {\"stringValue\": \"US\"}},\n    {\"name\": \"EffectiveDate\", \"operator\": \">=\", \"value\": {\"stringValue\": \"2026-01-01T00:00:00Z\"}}\n  ],\n  \"pricing\": {\"unitAmounts\": {\"USD\": 10.00}}\n}\n```\n\n**Schedule a future price change (preserve history):** submit a new row for the SAME business attributes with a later `EffectiveDate`. Do NOT delete the old row — the system automatically closes the previous price to a bounded window ending 1 second before the new start, and the new row becomes the open-ended current price.\n```\n\"rateCards\": [\n  {\n    \"attributes\": [\n      {\"name\": \"Region\", \"operator\": \"==\", \"value\": {\"stringValue\": \"US\"}},\n      {\"name\": \"EffectiveDate\", \"operator\": \">=\", \"value\": {\"stringValue\": \"2026-01-01T00:00:00Z\"}}\n    ],\n    \"pricing\": {\"unitAmounts\": {\"USD\": 10.00}}\n  },\n  {\n    \"attributes\": [\n      {\"name\": \"Region\", \"operator\": \"==\", \"value\": {\"stringValue\": \"US\"}},\n      {\"name\": \"EffectiveDate\", \"operator\": \">=\", \"value\": {\"stringValue\": \"2026-04-01T00:00:00Z\"}}\n    ],\n    \"pricing\": {\"unitAmounts\": {\"USD\": 12.00}}\n  }\n]\n```\nResulting timeline: US → $10.00 for `[2026-01-01, 2026-03-31T23:59:59]`, then US → $12.00 from `2026-04-01` onward.\n\nNotes:\n- Rows are grouped by their business attributes only (`EffectiveDate` is excluded from the grouping key), so each dimension combination carries an independent timeline.\n- Two rows with the same business attributes AND the same effective date are a duplicate and will be rejected.\n- Re-submitting a row whose pricing equals the price already in effect at its effective date is a no-op and is dropped — it will not add a redundant row.\n\n### Step 5: Verify creation\n\n**Query charge metadata with pagination:**\n\nRate cards default to 10 rows per page. Always use `rateCardPagination` to retrieve all rows:\n```\nTool: manage_commerce_charges\noperation: query_charge\nqueryJson: {\n  \"productRatePlanChargeKey\": \"<charge-id>\",\n  \"rateCardPagination\": {\"page\": 1, \"pageSize\": 50}\n}\n```\n\nIf the charge has more rows than `pageSize`, paginate through all pages to get the complete rate card.\n\n**Test dynamic pricing evaluation** (use plain values, NOT wrapped):\n```\nTool: manage_commerce_charges\noperation: query_charge\nqueryJson: {\n  \"productRatePlanChargeKey\": \"<charge-id>\",\n  \"attributes\": [{\"name\": \"Region\", \"value\": \"US\"}]\n}\n```\n\nReport the created entity IDs and confirm pricing evaluates correctly.\n\n## Updating existing rate cards\n\n### Update prices\n\n1. Get tier IDs:\n```\nTool: query_objects\nobjectType: \"ProductRatePlanChargeTier\"\nfilter: [\"ProductRatePlanChargeId.EQ:<charge-id>\"]\nfields: [\"Id\", \"Tier\", \"Price\", \"Currency\"]\n```\n\n2. Update price:\n```\nTool: manage_commerce_charges\noperation: update_tier_price\ntierJson: {\"id\": \"<tier-id>\", \"price\": 12.50}\n```\n\n### Add a new value option to an existing attribute (BCS)\n\n1. Call `manage_commerce_context_attributes` with `update_attribute_values` to add the new valid value.\n2. Then update the charge to add a new rate card row (see below).\n\n### Add a new row to the rate card\n\nFirst query the existing rate cards with pagination to get all rows:\n```\nTool: manage_commerce_charges\noperation: query_charge\nqueryJson: {\n  \"productRatePlanChargeKey\": \"<charge-id>\",\n  \"rateCardPagination\": {\"page\": 1, \"pageSize\": 50}\n}\n```\n\nThen call `manage_commerce_charges` with `update_charge`, providing the full updated `rateCards[]` array including existing rows plus the new one:\n```\nTool: manage_commerce_charges\noperation: update_charge\nchargeJson: {\n  \"charge\": {\n    \"id\": \"<charge-id>\",\n    \"rateCards\": [\n      ... existing rows ...,\n      {\n        \"attributes\": [\n          {\"name\": \"Region\", \"operator\": \"==\", \"value\": {\"stringValue\": \"LATAM\"}}\n        ],\n        \"pricing\": {\"unitAmounts\": {\"USD\": 7.00}}\n      }\n    ]\n  }\n}\n```\n\n### Change a price effective a future date\n\nTo change the price of an EXISTING attribute combination on a schedule (rather than adding a new dimension value), add a new rate card row for the same business attributes with a later `EffectiveDate` — see the effective dating section above. Keep the existing rows in the submitted `rateCards[]`; the system truncates the prior price window automatically and preserves it as history. Do not use `update_tier_price` for scheduled changes — that overwrites the current price in place with no history.\n\n## Critical constraints\n\n- `pricing.flatAmounts` is a map `{\"USD\": 99.99}`, NOT an array\n- Tiered: ranges must not overlap; last tier omits `upTo` — EXCEPT `tiered_overage` where ALL tiers require `upTo`\n- Cannot change `productRatePlanId` after charge creation\n- Tier IDs NOT returned in charge create/query — must use `query_objects` on `ProductRatePlanChargeTier`\n- `billCycle.timing`: include for recurring/one_time (`in_advance` or `in_arrears`), OMIT for usage\n- `billCycle.periodAlignment`: use `align_to_charge`\n- `endDateCondition`: required, typically `subscription_end`\n- For `update_charge`, use `fieldsToNull` array to explicitly clear fields\n- Rate card `attributes` array in criteria (NOT `attribute_values` or `criteria`)\n- Rate card values use `catalog.Value` wrappers; `query_charge` evaluation uses plain values\n- Mapped fields must exist on the object before attribute creation (standard fields already exist; custom fields must be created first)\n- Execute in order: custom fields (if needed) → context attributes → product → plan → charge\n- Consolidate pricing variations into rate cards — don't create separate products\n- `EffectiveDate` is a reserved attribute — never declare it in charge-level `attributes[]`, never map it to a field, never create a custom field for it; use it only inside a rate card's `attributes[]` criteria\n- `EffectiveDate` accepts only the `>=` operator; value must be ISO-8601 with a zone offset (e.g. `2026-04-01T00:00:00Z`); omitting it means effective from now\n- For a scheduled price change, add a new effective-dated row for the same attributes and keep the existing rows — the system computes the end of the prior window; do not delete old rows or use `update_tier_price`\n- Same business attributes + same effective date across two rows is a duplicate (rejected); a row identical in price to what's already in effect is dropped as a no-op\n"
}

SHA-256 of public snapshot: 957720f49e179006fd615e8e8467456fe2e2d68c9aeb91c496453c95a7be7b89