This walkthrough builds a real subscription-cancellation workflow on Fini, end to end. By the time you finish, a customer typing “cancel my subscription” will get a deterministic response: the agent verifies their intent, extracts their cancellation reason, looks up their account, calls your billing API to execute the cancellation, and replies with the real refund amount and effective date, every value pulled from your live system, every step recorded in the AI Steps trace. The flow you’ll build is 100% deterministic: given the same conversation context, it produces the same tree walk, the same API call, the same Reply every time. That’s the whole point of Rulebook, for workflows like cancellations, refunds, identity verification, or claims, you don’t want an LLM improvising. You want a precise, auditable sequence the LLM only kicks off when the intent matches. This walkthrough configures the Behavior surface of your agent (the deterministic rules) and touches Observability (the AI Steps trace). Every field, button label, and condition value below is what you’ll actually see and type in the dashboard.
Use this walkthrough as the canonical reference for any intent → identify customer → execute one API call → confirm flow. Refunds, account closures, subscription pauses, and password resets all share the same four-piece shape. Pick the closest companion walkthrough if your flow needs different machinery: Card replacement for chained Tools + Form input, Order status and changes for Fallback-rooted multi-intent rules.

Before you start

This walkthrough configures the Behavior layer of your Fini agent (the deterministic rules in Rulebook and the Reply Behavior gates around them). It assumes the other three layers your agent needs to be operational are already in place: Knowledge so the agent has something to fall back on when this rule doesn’t fire, Prompts so the Reply nodes inherit your tone and escalation rules, and Deployment so customers can actually reach the agent. If you’re starting from scratch, run Quickstart first, it walks you through all three in order. You’ll also need a handful of cancellation-specific things in place: If you don’t have a billing API ready and want to follow along anyway, swap the URLs for your own test endpoints and the rest of the walkthrough still works.
Idempotency. Cancellations are destructive. If your billing API supports idempotency keys (Stripe, Recurly, Chargebee all do), pass one in the Action’s body, that way a duplicate Tool call (e.g., the customer sends the cancel message twice before the first reply arrives) won’t double-cancel. The Tool’s Body field accepts any JSON you write, so add "idempotency_key": "${customer_id}-${effective_date}" or similar.

What you’ll build

A subscription-cancellation flow that:
  1. Identifies the customer on every message by looking up their email in your billing system, exposing their customer_id, plan, and an is_vip flag to the agent.
  2. Cancels the subscription when the customer asks to, by calling your billing API with the customer’s ID and reason.
  3. Orchestrates the conversation with a Rulebook tree that gates on intent, extracts the cancellation reason, calls the cancel Action, and replies with the confirmation details.
  4. Holds VIP cancellations for human review by posting an internal note instead of replying directly when the customer is on an Enterprise plan.
The final rule looks like this:
Cancellation flow rule tree. Root Steps node contains five children in order: Check verifying intent is cancellation, Read extracting the cancellation reason, Check verifying customer_id is not null, Tool calling the Cancel Subscription action with customer_id and reason as input and binding confirmation_id, refund_amount, and effective_date as output, and Reply composing a confirmation message with those output values interpolated.
Reading the tree top to bottom: the root Steps node runs children in order and short-circuits on the first failure. The intent Check at the top filters out non-cancellation messages, a deterministic backstop on top of the LLM’s intent routing. The Read uses the LLM to extract the customer’s stated reason into a tree variable. The customer_id Check is a precondition, without an identified customer, the rule stops cleanly. The Tool calls the Cancel Subscription action with two typed inputs and binds three typed outputs as new tree variables. The Reply at the bottom composes the confirmation message, interpolating the Tool’s outputs into the final text. The four pieces and where each is configured: The order below is also the order you should build in, each piece references the prior one.

Step 1: Configure the User Attribute

The first thing the agent needs to know is who is this person?. User Attributes are Fini’s mechanism for pulling that context in. On every message, Fini calls your configured HTTP endpoint, parses the response, and exposes the named fields to the agent, so the agent always sees the current plan, is_vip, and customer_id without you having to push updates to Fini.
1

Create the attribute

On API Setup → Attributes, click New Attribute. Name it Customer Identity. Pick a Source:
  • ui if your app passes the customer’s email through UI metadata.
  • widget if you sign the email into a JWT for the Fini widget.
  • The integration’s own name (e.g., zendesk, intercom, front, gorgias, hubspot, salesforce, livechat) if your traffic flows through a connected helpdesk, the helpdesk’s user identifier becomes the lookup key.
2

Declare the input field

Under UI Metadata Attributes (or JWT Token Attributes for widget source), list email with Use as Input in API turned on. This tells Fini to pass email to the HTTP call as ${email}. The other two switches can stay off for the email itself, you don’t need to match rules on it or surface it to the LLM, you just need to use it to look up the customer.
3

Add a Data Collection Step

A Data Collection Step is the HTTP call that fetches the customer data. Configure it:
  • Name: Lookup Customer
  • Method: GET
  • URL: https://api.yourbilling.com/customers?email=${email}
  • Headers: {"x-api-key": "your-billing-api-key"} (per Fini’s API contract when calling your own endpoint; if you’re calling a third-party SaaS billing API like Stripe or Recurly, use their auth scheme instead, typically {"Authorization": "Bearer ..."}).
  • Save From Response: {"customer_id": "id", "plan": "plan", "is_vip": "is_vip"}
The Save From Response block turns your API’s JSON response into named fields. Keys on the left are the field names you’ll reference in the rest of Fini; values on the right are the JSON paths into the API response.
Where the API token lives. Fini doesn’t have a separate “workspace secrets” store for custom Attribute calls; the credential is hardcoded in the Headers JSON here. The Fini workspace itself is the secret boundary. Native integrations expose their own connection fields only for the matching integration source, and selected active integration fields can be used by widget-source attributes.
4

Toggle the collected fields

Collected fields (those populated by your Data Collection Step) have two switches, Use in Rulebooks and Visible to AI. They’re automatically available to subsequent Data Steps in the same chain by name, so there’s no separate “Use as Input in API” switch on collected fields.The pattern: turn on Use in Rulebooks when Rulebook or Reply Behavior conditions need to match on the field. Turn on Visible to AI only when you want the LLM to know the value and potentially reference it in replies. customer_id stays out of LLM context, the agent should never quote an internal ID back to the customer. is_vip is a system flag for rules, not customer-facing.Downstream Actions that consume customer_id pick it up via the Tool node’s input binding in the Rulebook (covered in Step 3), not via a Use-as-Input-in-API toggle here.
5

Attach to your agent

Pick the agent from the top-right dropdown, toggle Customer Identity on, leave Chat and Email both selected (so the attribute fetches on both channels), and click Save.
6

Verify the lookup

Click Play on the Data Collection Step. Plug in a real email from your dev environment and run the lookup. Confirm the response includes all three expected fields (customer_id, plan, is_vip) with the right values. If anything’s off, fix it here, every downstream stage assumes this lookup works.
  • The Data Step returns 401 / 403: the secret token isn’t configured or doesn’t have the right permissions. Re-check the workspace secret value and the API’s auth requirements.
  • The response mapping returns null fields: the JSON path on the right side of the mapping doesn’t match your API’s actual response shape. Run the API call directly (curl, Postman) and check the field names.
  • The attribute works in test but not in production: the agent isn’t attached. Confirm the attribute is toggled on for the right agent under the agent picker.
For the full reference on attribute switches, sources, and Data Collection Steps, see Configuration → Attributes.

Step 2: Configure the Action

The Attribute pulls customer state in; the Action pushes a change out. In this case, the change is cancel this customer’s subscription. Actions are workspace-level, you configure them once, and any rule in any agent can invoke them via a Tool node. Each Action declares an Input schema (the typed parameters it accepts) and an Output schema (the typed fields it returns). Those schemas are how the Rulebook validates Tool nodes at design time, type mismatches surface in the editor, not at runtime.
1

Create the action

On API Setup → Actions (page header reads External Actions), click New Action. Configure:
  • Name: Cancel Subscription
  • Description: Cancels the customer's active subscription via the billing API. Returns confirmation id, refund amount, and effective date.
The Description here is for your team, it shows up when teammates browse available Actions. Unlike a Rulebook rule description, it’s not used for LLM routing.
2

Define the Input schema

The Input schema declares what the Rulebook must provide when invoking this Action:Marking customer_id required means a Tool node referencing this Action won’t validate unless it binds something to customer_id. reason being optional lets the rule call the Action even when the customer didn’t volunteer one.
3

Define the Output schema

The Output schema declares the typed fields the Action returns. These become tree variables in Rulebook, available to downstream nodes.Note the types: refund_amount as number means downstream Checks can use numeric operators on it (e.g., refund_amount Greater Than 100). effective_date as string because most billing APIs return ISO date strings; downstream Replies can interpolate it as-is.
4

Add the Data Step

The Data Step is the actual HTTP call:
  • Step Name: Cancel via Billing API
  • Method: POST
  • URL: https://api.yourbilling.com/v1/subscriptions/${customer_id}/cancel
  • Headers: {"x-api-key": "your-billing-api-key"} (or your third-party billing system’s Bearer scheme).
  • Body: {"reason": "${reason}"}
  • Save From Response: {"confirmation_id": "confirmation_id", "refund_amount": "refund.amount", "effective_date": "effective_at"}
Save From Response handles common shape mismatches: your billing API might return refund.amount (nested) but you want to expose it as a flat refund_amount to Rulebook.
5

Test, then save

Click Play in the Data Step. Plug in a real customer_id from your dev environment (one with an active test subscription you can safely cancel) and run the step. Confirm:
  • The HTTP call returns 200.
  • All three output fields populate with the expected types.
  • The response shape matches your declared Output schema.
If the shape doesn’t match, fix the response mapping here. Don’t move on until the test passes, every downstream stage assumes the Action works.
  • The test returns 404 / 422: the URL or body shape is wrong for your billing API. Compare against your API’s docs.
  • The Play test won’t accept your customer_id: the Input schema is more strict than the test panel. Verify customer_id is marked as a string, not a number.
  • Outputs don’t bind: the response mapping references a JSON path that doesn’t exist. Pull a real response from your API and update the right-hand-side paths.
For the full reference on Input/Output schemas, chaining Tool nodes, and the workspace-vs-rule scoping model, see Configuration → Actions.

Step 3: Configure the Rulebook rule

The Attribute brings customer context in; the Action pushes a change out; the Rulebook decides when the change happens and how the conversation flows around it. This is the orchestration layer. Each rule is a behavior tree that the LLM may invoke when the customer’s message matches the rule’s Description. Inside the tree, deterministic node types compose the work, Checks gate, Reads extract, Tools execute, Replies compose.
Tree reference. You’ll build this node by node in the steps below. Final shape:
1

Create a new rule

On Automations → Rulebook, click + Create Rule. Set the Name to Cancellation flow.
2

Write the Description carefully

The Description is how the LLM decides whether to fire this rule on a given customer message. A vague description (“Handles billing things”) will misroute messages or skip yours entirely. Write it like a trigger specification.For this walkthrough, paste this as your starting Description:
“Use this rule when the customer wants to cancel their subscription. The query could look like: ‘cancel my subscription,’ ‘I want to close my account,’ ‘stop charging me,’ or ‘I’d like to terminate my plan.’”
Tighten the example phrases over time to match how your customers actually phrase cancellation requests. Imagine you’re teaching a new teammate what triggers this rule, that’s the level of specificity to aim for.
The Description field is the rule’s only routing signal. If a customer’s message doesn’t seem to fire your rule in production, the Description is the first thing to revisit. You can confirm what the LLM did in the Planning section of the AI Steps trace, see the Test before publishing step below.
3

Add the intent Check

Click + Add step on the root Steps node. Pick Check as the node type. Configure:
  • Name: Intent is cancellation
  • Condition group: in the field picker, expand Tag Groups and pick Type of Issue (the default group) or your custom routing group (e.g., Topic). Operator Equals. Value Cancellation.
This is the deterministic backstop on the LLM’s routing. Even if the Description matches and the LLM picks this rule, the Check short-circuits if the conversation’s tag isn’t Cancellation. This protects against edge cases where the LLM routes overconfidently.
Set up the routing tag group first. Tag Groups are populated by the LLM after it classifies each conversation. For this Check to work, the tag group needs four things in place under Configuration → Tags: (1) Tag Group available in Rulebooks turned on; (2) Tag Selection set to “Exactly one tag” (otherwise use Contains, not Equals); (3) a Cancellation tag defined, with its description teaching the model when to apply it; (4) the group assigned to your agent under the agent selector. The default Type of Issue group ships with a Cancellation-style tag and is the path of least resistance; create a custom Topic group only if you need different categories.
4

Add the Read for the cancellation reason

Click + Add step again. Pick Read. Configure:
  • Name: Extract cancellation reason
  • Look at: Interaction History
  • Find these details: field reason, type String
  • Instructions: “Extract the customer’s stated reason for cancelling. Examples include ‘too expensive,’ ‘not using it,’ ‘product not working,’ ‘switching to competitor.’ If no reason is given, return an empty string.”
Concrete examples in the instructions dramatically improve extraction reliability. The Read populates a tree variable named reason that downstream nodes can reference as ${reason}.
Read or Form? Use Read when the customer typically volunteers the reason in their message (the cancellation case). Use Form when you need the reason structured for downstream reporting (e.g., dropdown of Too expensive / Not using it / Switching competitor / Other), or when the cancellation is sensitive enough that you want a typed, auditable record of the customer chose Reason X. The address-change example in Rulebook shows the Form pattern in full.
5

Add the customer_id Check

Click + Add step. Pick Check. Configure:
  • Name: Customer is identified
  • Condition group: in the field picker, expand User Attributes → pick customer_id. Operator Is Not Null.
This Check references the customer_id field that was exposed by the Customer Identity attribute in Step 1 (which is why it had Use in Rulebooks turned on). If the customer’s email lookup didn’t return a customer_id, they’re not in your billing system, or the API call failed silently, this Check short-circuits the rule cleanly instead of calling the cancel API with a null ID.
6

Add the Tool node and bind its inputs

Click + Add step. Pick Tool. In the configuration panel:
  • Action: select Cancel Subscription from the dropdown (the Action you created in Step 2).
  • Inputs: the panel shows the Action’s required inputs. For each one, pick where the value comes from:
    • customer_id → bind to customer_id (from the User Attribute)
    • reason → bind to reason (from the upstream Read node)
The binding is the load-bearing part. The dropdown for each input shows every tree variable available at this point in the tree, User Attributes, upstream Read outputs, upstream Tool outputs. Pick the one whose name and type match the Action’s input.Once you’ve bound the inputs, the Tool’s outputs (confirmation_id, refund_amount, effective_date) automatically become tree variables available to nodes below this one.
7

Add the Reply node

Click + Add step. Pick Reply. In the Instructions field, write:
“Tell the customer their subscription has been cancelled. Mention that a refund of refundamountwillbeprocessedby{refund_amount} will be processed by , and give them the confirmation ID $ for their records.”
The ${variable} placeholders interpolate from the Tool’s outputs. The Reply node injects this as instructions into the agent’s reply-composition pass, the agent writes the final wording in its own voice, but the content (refund amount, dates, confirmation ID) comes from the bound variables.
Reply instructions don’t have to be the verbatim message to the customer. They’re guidance for the agent. “Tell the customer…” works better than copy-pasting the exact reply text, because it lets the agent maintain its voice while reliably surfacing the facts you specified.
Voice control lives elsewhere. Reply instructions are for content directives (“mention the refund amount”, “share the confirmation id”). For voice (“be formal”, “never apologize twice”, brand vocabulary), edit your agent’s Main Guidelines → Tone instead, those rules apply to every reply your agent sends, including the one this rule produces.
8

Assign to your agent

Under Assigned Bots, pick the same agent you attached Customer Identity to in Step 1. A rule with no agents assigned never runs.
9

Test before publishing

Open the ▶ Test Run panel from the rule editor. Two input modes:
  • Conversation Link, paste a URL to a real conversation from your Inbox. Use this when you want to verify the rule behaves correctly on conversations you’ve already seen.
  • Custom JSON, paste a JSON object describing a synthetic conversation context. Use this for edge cases.
Start with Custom JSON. Paste something like:
Use the customer_id from the test you ran in Step 2’s Action, that’s a customer your billing API actually knows about. To test the VIP path you’ll configure in Step 4, set "is_vip": true instead and re-run. Click Run Test and walk the execution trace. A successful run looks like this:
AI Steps panel showing the execution trace for a successful cancellation. Five collapsible sections: Planning (collapsed, showing it routed to Cancellation flow), Executed Rule (expanded), Generate Answer, Input Tag Selection, and Output Tag Selection. Inside Executed Rule, five rows, each with a green status dot, the node title, and a colored type pill. The five rows in order are: Intent is cancellation (Check), Extract cancellation reason (Read) showing reason value, Customer is identified (Check), Cancel Subscription (Tool) showing typed inputs and outputs with real values, and Confirm cancellation (Reply).
What to verify:
  1. The intent Check passes (green dot).
  2. The Read populates reason with something like “product just isn’t working”.
  3. The customer_id Check passes.
  4. The Tool fires with the expected inputs and returns three output fields with real values.
  5. The Reply interpolates the values correctly.
If a node shows a red dot, click into it to see what value it had. Reads not populating means the extraction instructions aren’t clear enough; Checks failing means the upstream data isn’t what you expected.
10

Publish

Save commits the draft. Publish promotes it to live. From the next conversation onward, your agent will run this rule on messages whose Description matches.
  • The rule doesn’t fire on test conversations: the LLM didn’t route to your rule’s Description. Check the Planning section of the AI Steps trace to see which rule it picked (if any) and rewrite the Description with more example trigger phrases.
  • The Read returns nothing: the extraction prompt isn’t specific enough. Edit the Read to say, e.g., “Extract the customer’s reason for cancellation, or return an empty string if no reason is given.”
  • The customer_id Check fails: the User Attribute from Step 1 isn’t populated for this conversation. Verify the agent is attached to the attribute and the email lookup is returning customer_id.
  • The Tool fails with auth errors: the workspace secret isn’t set or isn’t readable from this surface. Re-check the secret config.
For the full reference on node types, tree variables, the AI Steps trace, and tool chaining, see Automations → Rulebook.

Step 4: Configure the Reply Behavior safety net

You’ve now got a working cancellation flow. This step adds an optional safety override on top: for Enterprise customers (the is_vip flag from Step 1), the agent should draft the cancellation reply but post it as an internal note instead of sending it to the customer, so a human can review before it goes out. This step is optional. The flow you built above works without it. But for high-stakes intents like cancellations, refunds, or account closures, especially with high-value customers, having Reply Behavior gate the final send is a common pattern in regulated industries.
1

Open the Internal Comment card

On Automations → Reply Behavior (sidebar item: Reply Rules), click the Internal Comment card to expand it.
2

Add the two predicates

In the first condition group on the card, add two predicates:
  • User Attributes → is_vip Equals True
  • Tag Groups → Type of Issue Equals Cancellation (or your custom routing group, using Equals if Tag Selection is “Exactly one tag” or Contains if “Multiple tags can be selected”).
Predicates inside the same group are joined with AND, so both must be true for the rule to apply. Don’t click + Add alternative condition, that creates a new group that’s OR’d with the existing one, which isn’t what we want here.The first predicate uses the is_vip field you exposed in Step 1 (which is why it had Use in Rulebooks turned on). The second uses the same tag group the Rulebook Check reads, the LLM populates it after classifying the conversation. The three cards (No Reply, Internal Comment, Direct Reply) evaluate independently; the stricter behavior wins (No Reply > Internal Comment > Direct Reply).
3

Enable and save

Toggle the Internal Comment switch on in the card’s top-right. Click Save.
Reply Behavior gates the agent’s final send decision, which doesn’t surface in the Rulebook Test Run. You’ll verify it works in the next section by sending a real test message from a VIP customer account and confirming the agent posts an internal note rather than a direct reply.
What this rule does at runtime: when a VIP asks to cancel, the agent still runs the Rulebook flow from Step 3, the cancellation API call still fires, and the Reply node still drafts the confirmation message. But because Reply Behavior matched the Internal Comment card, the drafted message gets posted as an internal note visible only to your team. A human can read it, optionally call the customer, and post the customer-facing response themselves. The exact terminology varies by integration, Internal Note in Zendesk and Intercom, Internal Comment in Front, Case Comment in Salesforce. The behavior is the same: customer-invisible. Why Internal Comment, not No Reply? Both prevent a direct customer reply, but they do different work:
  • Internal Comment lets the agent run the full Rulebook flow and draft a reply your team can review. The API call still happens. Useful when you want the work done but a human to vet the wording.
  • No Reply would stop the agent from generating anything at all. The API call wouldn’t fire, the conversation would sit untouched, and you’d handle everything manually.
For VIP cancellations specifically, Internal Comment gives your team a head start (the API is done; the message is drafted) without the risk of a tone-deaf auto-reply going to a customer who might be saved by a phone call.
  • The card doesn’t enable: there’s a predicate row with no value selected. Walk through and confirm every field, operator, and value is filled in.
  • The Tag Group predicate doesn’t fire on test conversations: Tag Groups are populated by the LLM after it classifies the conversation. For brand-new test conversations, the tag may not exist yet. Use the AI Steps panel in the Inbox to check whether the Topic tag was applied.
  • Internal Comment fires but No Reply was also expected: if you have a No Reply rule that also matches, it takes priority. No Reply > Internal Comment > Direct Reply.
The interaction with the Rulebook is the key insight: Rulebook decides what happens; Reply Behavior decides who sees what the agent generated. They’re orthogonal layers.
Reply Behavior is one of two safety surfaces. The other is your agent’s Planning Prompt → Escalation Topics, which routes whole conversations to a human before any rule fires. For high-stakes intents like cancellations, you can layer both: Escalation Topics catches the conversations the LLM judges as fraught (e.g., “customer is threatening churn for a competitor”); Reply Behavior catches the deterministic predicates (e.g., is_vip Equals True). Use the Prompt for fuzzy judgment; use Reply Behavior for hard rules.
For the full reference on reply types, priority order, and the condition builder, see Automations → Reply Behavior.

Verify the flow end-to-end

With everything saved and published, send three test messages from a real conversation surface, the widget on your site, a test Zendesk ticket, an Intercom test conversation, whatever channel you’ve integrated your agent into. Use a test customer account whose email resolves through your billing API so the Customer Identity attribute populates real values.
  1. Non-VIP, cancellation intent. Send “please cancel my subscription, the product isn’t working for us” from a test customer whose is_vip is false. Within seconds, you should see a direct reply in the channel with the real refund amount interpolated from the Action’s output.
  2. VIP, cancellation intent. Same message, but from a test customer whose is_vip is true. The Rulebook still runs, the cancellation API call still fires, the reply is still drafted, but the reply posts as an internal note instead of a direct customer reply. A human teammate can review and follow up.
  3. Non-cancellation intent. Send “how do I add a new card?” from any customer. The LLM doesn’t route to your Cancellation flow rule (because the Description doesn’t match), the rule’s tree never runs, and the agent answers from its knowledge base instead. The intent Check inside your tree is a backstop for cases where the LLM does route here on a borderline message, it almost never triggers in normal operation.
The Knowledge fallback matters more than people realize. When the intent Check fails, when customer_id Is Not Null fails, when the Tool errors, when the customer’s message routes away from this rule entirely, the agent answers from your Knowledge base. For this walkthrough to feel complete to customers, make sure you have Articles covering the fall-through paths: “We can’t process cancellations for closed accounts”, “We can’t find your account, please reach out from the registered email”, and the actual cancellation / refund policy text the agent will reference if the Tool fails mid-flow. Background AI watches conversations and proposes Articles for gaps it detects, check the Review queue weekly.
Verify in the AI Steps trace. Open each test conversation in the Inbox and look at the AI Steps panel on the right. You should see the same trace shape as the one shown in Step 3’s Test step:
  • For tests 1 and 2: a green-dot trace through all five rule nodes, with the Tool node showing the real API output values.
  • For test 3: the Planning section shows the LLM didn’t pick the Cancellation flow rule, and the Executed Rule section is empty, no rule ran, the agent answered from knowledge.
The trace is your source of truth in production. Any time the rule behaves unexpectedly, open AI Steps and find the first red dot.
What happens on follow-up messages in the same conversation? Each customer message is evaluated independently, the rule re-runs from the root every time. So if the customer cancels in one message and then sends “actually wait, can I get a discount instead?” in a follow-up, the rule will fire again on the second message (assuming the intent still matches). For cancellation specifically, the API call from the first message has already executed by the time the second message arrives. Two defensive patterns:
  1. Confirm before cancelling. Add a Form node before the Tool that requires explicit customer confirmation (a single “Confirm cancellation” button). The rule short-circuits cleanly on the first message if the customer hasn’t confirmed.
  2. Idempotent cancel API. Pass a deterministic idempotency key (e.g., ${customer_id}-${effective_date}) in the Action body so a duplicate Tool call returns the same confirmation_id without performing the cancellation twice. Most modern billing APIs support this header.

Lock it down with a regression test

Once the rule is live, add it to your Test Suite. Generate scenarios from a real cancellation transcript and bind one of Fini’s six pre-built judges, Tool use correctness is the natural fit for cancellation (did the Cancel Subscription Action fire on the right input?), pair it with Goal resolution if you want to also grade whether the customer ended up with their stated outcome. Scenarios are simulated multi-turn conversations, so the expected outcome should describe the arc (“agent acknowledges, calls the cancel action with the customer_id, confirms with the refund amount”) rather than verbatim wording. Re-run the set every time you change the Description, the Read instructions, or the Action’s Data Step.
Test Suite runs hit your real APIs. Each scenario replay actually invokes the Tool, which means a Cancel Subscription run will fire your billing API for real. Either point the Action at a sandbox endpoint while you’re iterating on the test set, or accept that each run produces real cancellations on test accounts. The Test Suite page calls this out, worth re-reading before you run on production credentials.
After a week in production, open Analytics → Overview and find the row for Cancellation (or your routing intent) in the Category breakdown table. The row shows volume, AI resolve rate, and escalated rate for that intent specifically; click the row to spot-check conversations. If the escalated rate is climbing while the rule keeps firing, the issue is in the Reply, the Action, or downstream Reply Behavior, open those conversations in the Inbox and walk the AI Steps trace for each one.

What to vary

Once the basic flow is in place, common adaptations:
  • Add a Form for structured reason collection. Replace the Read node with a Form node that gives the customer a dropdown of cancellation reasons plus a free-text “other” field. Read works when the customer volunteers the reason in their message; Form is better when you need structured, required input you can analyze later (e.g., reporting on cancellation reasons). The form’s submitted values become tree variables the Tool can reference. See Rulebook → A worked example: address change with Form.
  • Chain multiple Tools. Add a Verify Refund Eligibility Tool before Cancel Subscription, gate the cancel Tool with a Check on the eligibility output, and handle the denial path with a Reply explaining why. See Rulebook → Chaining Tools.
  • Different VIP definition. Replace the is_vip Equals True predicate with a tier check on plan (e.g., plan Equals Enterprise), or layer multiple conditions for tiered escalation.
  • Stay silent for VIPs instead of drafting. Switch the Reply Behavior rule from Internal Comment to No Reply if you want the agent to not engage at all for VIPs, leaving the conversation entirely to humans.
  • Add graceful recovery. Wrap the cancellation Tool in a Fallback whose second child is a Reply explaining what happened, so a failed API call gives the customer a useful response rather than escalating silently.
The pattern (User Attribute for context → Action for work → Rulebook for orchestration → Reply Behavior for global overrides) generalizes to most workflows: refunds, identity verification, account upgrades, address changes, claims intake, password resets, and so on. Anything where you’d write a runbook for a human agent maps cleanly to this four-piece structure.

Troubleshooting

Stages where this walkthrough commonly gets stuck, ordered by likelihood.
The most common cause: the rule is still a Draft. Drafts don’t run on live conversations, only Published rules do. Open the rule and click Publish. Less common but worth checking: the agent isn’t actually receiving messages (deployment / integration issue), or the Attribute isn’t fetching at runtime (test the attribute lookup from the agent’s deployed channel, not just the dashboard).
The LLM didn’t route to your rule’s Description. Open the conversation in the Inbox and check the Planning section of the AI Steps trace, it shows which rule (if any) the LLM picked. If the picked rule is wrong or none was picked, the Description on your rule needs tightening. Rewrite it with concrete example trigger phrases (see Step 3’s Description guidance).
The User Attribute from Step 1 isn’t populating customer_id for this agent. Three things to check:
  1. The attribute is toggled on for the correct agent under Configuration → Attributes with that agent selected in the picker.
  2. The email used in the lookup matches what’s actually being passed (UI metadata vs widget JWT vs integration user ID, depending on your Source).
  3. The API endpoint is returning a customer_id value, not null or undefined. Test the Data Collection Step’s lookup with a known-good email.
BILLING_API_TOKEN isn’t configured as a workspace secret, or the secret value is wrong, or the secret isn’t accessible from the Actions Data Step (separate from Attributes). Verify under workspace settings → Secrets that the value exists and matches what your API expects. The same ${SECRET_NAME} syntax works for both Attributes and Actions, but they reference the same workspace-level secret store, there’s no per-feature scoping.
The LLM read the conversation but didn’t find a reason it could extract. Two possibilities:
  1. The customer genuinely didn’t say why, “please cancel” is a valid cancellation message with no reason. The Read’s instructions should allow this: “Extract the customer’s reason if given; return an empty string otherwise.”
  2. The extraction prompt is too narrow. If the customer wrote “this product just isn’t working for us anymore”, the LLM might not classify that as a “reason.” Broaden the instructions with examples of what counts.
Your Reply node is referencing variables that don’t match the Tool’s output schema. Common mistakes:
  • Typos: ${refundAmount} vs ${refund_amount}, variable names are case-sensitive and exact.
  • Schema mismatch: the response mapping in Step 2 didn’t expose the field, so it’s not available as a tree variable. Re-verify the mapping.
  • Path errors: refund.amount in the response wasn’t flattened to refund_amount in the mapping. Check Step 2’s response mapping.
Two common causes:
  • A higher-priority card matched first. No Reply beats Internal Comment beats Direct Reply. If you have a No Reply rule that also matches (e.g., human agent assigned), it overrides Internal Comment. Audit your No Reply conditions.
  • The Topic tag isn’t populated yet. Tag Groups are populated after classification. On brand-new test conversations, the Topic tag may not exist, so the predicate evaluates to false. Open AI Steps → Output Tag Selection to confirm the tag was applied.
Once the Action calls your billing API, the cancellation is real. Fini doesn’t add an undo layer on top of your API. If you want a “soft cancel with grace period” pattern, that’s an API-side change, the Action just invokes whatever endpoint you point it at. Two safety practices for cancellation flows specifically:
  1. Use Reply Behavior to gate destructive Actions (Step 4’s pattern) so a human reviews before the customer sees the confirmation.
  2. Add an “Are you sure?” Form before the Tool node, requiring an explicit confirmation step.

Rulebook

Full reference for behavior trees, node types, the AI Steps trace, Test Run, and rule selection.

Inbox

Open any conversation and read the AI Steps trace to see exactly how the rule executed.

Analytics

Once your rule is live, monitor how often it fires, success/failure rates, and escalation counts.

Reply Behavior

The three reply types, condition builder, operators, and priority order.

Attributes

Sources, fields, switches, and Data Collection Steps.

Actions

Input/Output schemas, Tool chaining, and the workspace-vs-rule scoping model.

Test Suite

Run this rule against a curated set of conversations as a regression check before publishing changes.

Tags

Configure the Topic tag group the Check at the top of the tree references.

Card replacement

Companion walkthrough for fintech: chained Tools, Form input for shipping address, and a Reply Behavior fraud gate.

Order status and changes

Companion walkthrough for e-commerce: a Fallback-rooted rule that handles three intents in one tree.