Skip to main content
This walkthrough builds a real lost-or-stolen card replacement workflow on Fini, end to end. By the time you finish, a customer typing “I lost my card” or “my card was stolen, please send a new one” will get a deterministic response: the agent confirms which card to freeze (if the customer has more than one), collects a shipping address with a structured form, freezes the compromised card via your card-management API, orders a replacement, and replies with the expected delivery date and a tracking link, every value pulled from your live system, every step recorded in the AI Steps trace. The flow is 100% deterministic: given the same conversation context, it produces the same tree walk, the same two API calls in the same order, the same Reply every time. That property is non-negotiable for fintech, anything touching a payment instrument has to be auditable end to end, and the AI Steps trace is what makes it auditable. This walkthrough configures the Behavior surface of your agent (the deterministic rules) plus a fraud safety net via Reply Behavior. Every field, button label, and condition value below is what you’ll actually see and type in the dashboard.
Use this walkthrough when your flow needs more than one API call wired together, structured input from the customer (not just LLM extraction), and a fraud or risk gate before the destructive Action fires. If your flow is intent → single API call → confirm, see the simpler Cancellation flow walkthrough first.

Before you start

This walkthrough configures the Behavior layer of your Fini agent (the Rulebook tree, the two chained Actions, the Reply Behavior fraud gate). It assumes the other three layers your agent needs are already in place: Knowledge so the agent has substance for the fall-through paths (closed accounts, frozen-but-not-lost cards, security guidance), Prompts so the Reply nodes inherit your tone and your Planning Prompt’s escalation rules, and Deployment so customers can actually reach the agent. If you’re starting from scratch, run Quickstart first. Card replacement specifically also needs the following: If you don’t have a card-management API ready and want to follow along anyway, swap the URLs for your own test endpoints and the rest of the walkthrough still works.
This is a destructive flow against a financial product. Freezing the wrong card or shipping a replacement to the wrong address creates customer pain and potential fraud loss. Build this walkthrough end to end in a sandbox or staging environment first; do not point the Actions at production card-management endpoints until the Test Suite (Step 6) has passed against a representative set of conversations. The Reply Behavior fraud gate in Step 5 is a safety net, not a substitute for testing.

What you’ll build

A card-replacement flow that:
  1. Identifies the customer and their cards on every message via a single Customer Identity attribute with two chained Data Collection Steps: step 1 looks up the customer (account_id, account_status, is_high_risk, recent_replacement_count); step 2 uses that account_id to list the customer’s active cards (active_cards). One attribute, two HTTP calls in dependency order.
  2. Confirms which card to replace when the customer has more than one active card, by extracting a card_last_four from the message or asking via a quick disambiguation reply.
  3. Collects a shipping address with a Form node so the address is typed, validated, and recorded as a structured artifact on the conversation transcript.
  4. Freezes the compromised card via a Freeze Card Action, then orders a replacement via an Order Replacement Card Action, two chained Tools, with the freeze’s confirmation_id flowing into the order’s input.
  5. Gates fraud-risk replacements for human review by routing high-risk customers or repeat-replacement requests through Reply Behavior’s Internal Comment.
The final rule looks like this:
Card replacement rule tree. Root Steps node contains seven children in order: Check verifying intent is card-lost-or-stolen, Check verifying account_status is active, Read extracting card_last_four and replacement_reason, Form collecting shipping_street/shipping_city/shipping_postal_code/shipping_country with Form Error children for invalid postal code and missing fields, Tool calling Freeze Card with card_id input and freeze_confirmation_id output, Tool calling Order Replacement Card with account_id and freeze_confirmation_id and shipping address inputs and new_card_id/expected_delivery/tracking_url outputs, 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. Two Checks at the top gate on intent and account state (an inactive or closed account cannot order a card). A Read extracts the card’s last four digits (if the customer mentioned them) and the replacement reason. A Form collects the shipping address with validation. Two Tools run in sequence: freeze first, then order, the order Tool waits on the freeze’s confirmation so we never ship a replacement against an unfrozen card. The final Reply composes the confirmation with the new card’s expected delivery date and tracking URL. The pieces and where each is configured: Build in this order, each piece references the prior ones.
Bot Routing reminder. If your fintech serves cards via the widget (Form path) and via Zendesk email tickets (Reads-in-sequence path), those are two separate Fini agents. You’ll need to either assign this rule to both agents (and accept that the Form step degrades on the email agent), or maintain two variants, one Form-based for the widget, one Read-based for the email channel. See Deploy → Widget and the integration page for whichever helpdesk you use.

Step 1: Configure the Customer Identity attribute

The agent needs two pieces of context on every message: who the customer is (account state, risk flags) and which cards they have (so the replacement Tool can address the right card). Both come from a single attribute with two Data Collection Steps chained in dependency order, step 2 references a field collected by step 1.
1

Create the attribute

On API Setup → Attributes, click New Attribute. Name it Customer Identity. Pick a Source:
  • widget if your widget signs the customer’s identity into a JWT (the standard pattern for authenticated fintech, see Widget → Identify logged-in users for the payload shape).
  • ui if your app passes the customer’s email through UI metadata.
  • The integration’s own name (zendesk, intercom, front, gorgias, hubspot, salesforce, livechat) if you’re routing through a helpdesk.
2

Declare the input field

Under the Passed-in fields group (UI Metadata Attributes or JWT Token Attributes depending on Source), list email (or customer_token, whichever your widget signs) with Use as Input in API turned on. This is the lookup key step 1 will pass to your customer-management API.
3

Add Data Collection Step 1: Lookup Customer

  • Step Name: Lookup Customer
  • Method: GET
  • URL: https://api.yourbank.com/v1/customers?email=${email}
  • Headers: {"x-api-key": "your-cards-api-key"} (per Fini’s API contract when calling your own endpoint; use the third-party scheme if you’re calling a processor’s SaaS API directly).
  • Save From Response: {"account_id": "id", "account_status": "status", "is_high_risk": "risk_flags.kyc_recheck_required", "recent_replacement_count": "stats.card_replacements_30d"}
The four fields step 1 yields:
4

Add Data Collection Step 2: List Active Cards

Add a second Data Collection Step below the first:
  • Step Name: List Active Cards
  • Method: GET
  • URL: https://api.yourbank.com/v1/customers/${account_id}/cards?status=active
  • Headers: same as step 1.
  • Save From Response: {"active_cards": "cards"}
The ${account_id} in step 2’s URL is the field step 1 just collected, the docs are explicit about this: “later steps can use any field collected by earlier steps as input.” The fields collected by step 1 are automatically available to step 2 by name, no input switch needed.Your processor’s response should be an array shaped like:
The Read node downstream will use this array to disambiguate when the customer has multiple cards.
5

Toggle the collected fields

Collected fields (those populated by Data Collection Steps) have two switches, Use in Rulebooks and Visible to AI. They don’t show a “Use as Input in API” toggle, that switch only exists on passed-in fields and credentials.None of the operational flags get Visible to AI, they shouldn’t appear in customer-facing replies. active_cards does need to be visible so the agent can verbalize the disambiguation question naturally.
6

Attach to your agent

Pick the agent from the top-right dropdown, toggle Customer Identity on, leave Chat selected (Forms render reliably in the widget chat surface), and click Save.
7

Verify the chain

Click the Play button at the top of the detail view. Provide a real email from your dev environment and step through the chain. Confirm step 1 returns the four customer fields and step 2 returns a non-empty active_cards array. Each Data Collection Step also has its own per-step Play button you can use to test in isolation.
  • Step 2’s URL renders with literal ${account_id}: step 1 didn’t populate account_id. Click the per-step Play on step 1 and inspect the response, the Save From Response mapping’s right-hand path doesn’t match your API’s actual field name.
  • The cards array comes back empty for a customer you know has cards: your status=active filter is too narrow, or the processor treats frozen / blocked cards as non-active even when the customer regards them as their card. Adjust the URL filter, or expose multiple statuses in the array so the agent can show the customer their frozen card too.
  • Multiple cards but no last_four: your processor’s response uses a different key (pan_last_4, last4, mask). Fix the Save From Response mapping; the Read node below depends on the key being last_four exactly.
For the full reference on Attribute switches, sources, and chained Data Collection Steps, see API Setup → Attributes.

Step 2: Configure the two Actions

The Attribute pulls customer state in; two Actions push two changes out, in order. Both Actions are workspace-level, you configure them once, and any rule in any agent can invoke them via a Tool node.

2a. Freeze Card

1

Create the action

On API Setup → Actions (page header reads External Actions), click New Action. Configure:
  • Name: Freeze Card
  • Description: Freezes a specific card so it can no longer authorize new transactions. Returns a confirmation id for downstream actions to reference.
2

Define the inputs

reason is required by most card processors for compliance (the freeze gets recorded with a reason in the card’s audit log). Acceptable values are usually lost, stolen, compromised, customer_request.
3

Define the outputs

frozen_at is an ISO 8601 timestamp. The Reply doesn’t need it directly, but the next Action (Order Replacement Card) records it for the new card’s audit trail.
4

Add the Data Step

  • Step Name: Freeze card via Cards API
  • Method: POST
  • URL: https://api.yourbank.com/v1/cards/${card_id}/freeze
  • Headers: {"x-api-key": "your-cards-api-key"} (or your processor’s native scheme).
  • Body: {"reason": "${reason}", "idempotency_key": "${card_id}-freeze"}
  • Save From Response: {"freeze_confirmation_id": "id", "frozen_at": "frozen_at"}
Fini doesn’t enforce an idempotency convention, idempotency is a customer-side concern. The idempotency_key in the body is just an example pattern: a deterministic key keyed on the card so retries don’t double-freeze. Most processors take this from a header (Idempotency-Key) rather than the body, check your processor’s docs.
5

Test, then save

Click Play. Use a real card_id from your sandbox processor (with a test card you can safely freeze). Confirm the response returns 200 and both output fields populate.

2b. Order Replacement Card

1

Create the action

  • Name: Order Replacement Card
  • Description: Orders a replacement card on the customer's account, shipped to the address provided. Records the prior freeze confirmation for audit. Returns the new card id, expected delivery date, and tracking URL.
2

Define the inputs

Marking prior_freeze_confirmation_id required is the critical link: it forces the Rulebook tree to bind this input from an upstream Tool’s output, which means the freeze Tool must have run successfully before this Tool can validate. This is how Fini’s type system enforces “don’t ship a card unless the old one is frozen.”
3

Define the outputs

expected_delivery is ISO 8601 again. tracking_url is the carrier’s tracking page (UPS, FedEx, USPS, depending on your processor’s shipping partner). last_four is the new card’s last four digits, surfaced so the customer can match it when the card arrives.
4

Add the Data Step

  • Step Name: Order via Cards API
  • Method: POST
  • URL: https://api.yourbank.com/v1/customers/${account_id}/cards
  • Headers: {"x-api-key": "your-cards-api-key"} (or your processor’s native scheme).
  • Body:
  • Save From Response: {"new_card_id": "id", "expected_delivery": "shipping.estimated_delivery", "tracking_url": "shipping.tracking_url", "last_four": "last_four"}
The idempotency key is keyed on the freeze confirmation, so a retry against the same freeze produces the same replacement card rather than two.
5

Test, then save

Click Play. Plug in a real account_id and a recent freeze confirmation from your sandbox. Confirm the response returns 200 and all four output fields populate. Inspect the response carefully, shipping fields tend to be nested under different keys depending on the processor; adjust Save From Response accordingly.
  • The order test returns 422 with card_already_replaced : most processors block duplicate replacements against the same freeze. Use a fresh sandbox freeze, or wait for the processor’s cooldown.
  • tracking_url is null: some processors only populate tracking_url once the card actually ships (hours or days later). For the immediate confirmation reply, fall back to the expected_delivery date alone; document this in your Reply wording so customers don’t expect a live link the moment they get the message.
  • Shipping fails with invalid_country: processors typically use ISO 3166-1 alpha-2 codes (US, GB, IN). The Form node in Step 3 should constrain the country field to these codes, see the Form configuration below.
For the full reference on Action I/O schemas, chaining Tools, and the workspace-vs-rule scoping model, see Configuration → Actions.

Step 3: Configure the Rulebook rule

The Customer Identity attribute brings customer and card context in; the two Actions push the freeze and the order out; the Rulebook decides when the work happens, which card to act on, and how the conversation flows.
Tree reference. Final shape you’ll build below:
1

Create a new rule

On Automations → Rulebook, click + Create Rule. Set the Name to Card replacement.
2

Write the Description carefully

The Description is how the LLM decides whether to fire this rule on a given customer message. For this walkthrough, paste this as your starting Description:
“Use this rule when the customer reports a lost, stolen, damaged, or otherwise compromised card and wants a replacement. Example queries: ‘I lost my debit card’, ‘my card was stolen’, ‘someone took my wallet, please send a new card’, ‘my card stopped working, can you ship me a new one’, ‘I need a replacement card’. Do not use this rule for activation, PIN reset, or transaction disputes, those have their own rules.”
The negative examples at the end (“do not use this rule for…”) matter a lot when other card-related rules exist in the same workspace. The LLM uses the Description as a positive-and-negative signal.
The Description is the rule’s only routing signal. If a customer’s message doesn’t fire your rule in production, the Description is the first thing to revisit. Confirm what the LLM did in the Planning section of the AI Steps trace.
3

Add the intent Check

Click + Add step. Pick Check. Configure:
  • Name: Intent is card lost or stolen
  • Condition group: in the field picker, expand Tag Groups → pick Type of Issue (the default group) or your custom routing group. Operator Equals. Value Card Lost or Stolen.
The deterministic backstop on the LLM’s routing. Even if the LLM matches the Description and picks this rule, the Check short-circuits if the conversation’s tag isn’t Card Lost or Stolen, which catches false positives like “my card stopped working at the ATM” (an activation or PIN issue, not a replacement).
4

Add the account state Check

Click + Add step. Pick Check. Configure:
  • Name: Account is active
  • Condition group: in the field picker, expand User Attributes → pick account_status. Operator Equals. Value active.
Closed, suspended, or frozen accounts cannot order new cards. This Check short-circuits cleanly before any side-effect-bearing Tool fires, so a customer whose account was closed yesterday and is asking for a replacement today gets a clean “we can’t” path (default agent reply) rather than a half-executed transaction.
Refine this Check for your business. Some processors allow card orders on accounts in pending_kyc (the account exists but isn’t fully verified). If yours does, add an OR group: account_status Equals active OR account_status Equals pending_kyc. The condition builder supports OR via + Add alternative condition.
5

Add the Read for card and reason

Click + Add step. Pick Read. Configure:
  • Name: Extract card details
  • Look at: Interaction History
  • Find these details:
    • field card_last_four, type String, not required
    • field replacement_reason, type String, required, allowed values: lost, stolen, damaged, compromised
  • Instructions: “Extract the last four digits of the card the customer is reporting on, if they mentioned a number like ‘4242’ or ‘card ending in 1881’. If they didn’t mention a number, return an empty string, do not guess. Also extract the replacement_reason from these allowed values: lost, stolen, damaged, compromised. Examples: ‘I lost my wallet’ → lost. ‘someone stole my card at the gas station’ → stolen. ‘my card got cut in half by the ATM’ → damaged. ‘I saw a charge I didn’t make’ → compromised. If unclear, default to compromised.”
card_last_four is optional because customers often say “my card was stolen” without naming which card. When the customer has only one active card, the Tool will use that card’s id directly; when they have multiple, the agent will ask a disambiguation question before the Form renders.
Disambiguation when multi-card and no last_four: the dropdown logic happens in the Tool’s input binding (next step), not here. The Read just gathers what the customer volunteered.
6

Add the Form node

Click + Add step. Pick Form. Configure the Title as Shipping address for your replacement card, and add four required String fields (the Form node documents String, Number, Date as supported types, country is a single-line String that you’ll validate via Form Error):Add two Form Error children to the Form node:
  • Form Error 1: Postal code doesn’t match country format. Triggered when the entered shipping_postal_code fails the regex for the selected shipping_country. The Form Error specifies which field’s validation it represents; the form re-renders showing the error message until the customer corrects it.
  • Form Error 2: Street address too short. Triggered when shipping_street is fewer than 3 characters.
The customer fills the form, submits, and the four fields become tree variables (${shipping_street}, ${shipping_city}, ${shipping_postal_code}, ${shipping_country}) available to the Tools downstream.
Channel notes. Forms render reliably inside the Fini widget. Form rendering on helpdesk chat surfaces (Zendesk chat, Intercom Messenger, etc.) isn’t documented per-integration; default to the widget for Form-driven flows and confirm in your test channel before relying on it. On email deployments (Salesforce, Zendesk email-only), the Form node can’t render at all, replace the Form with four Read nodes in sequence (one per address field) and add a Reply between each to prompt for the next. Less elegant, but it works. The address-change pattern in Rulebook → A worked example: address change with Form is the canonical reference.
7

Resolve card_id and add the Freeze Card Tool

Tool inputs in Rulebook bind to a single tree variable, not a conditional expression. To pick the right card_id from a customer’s potentially-multi-card account, do the resolution upstream in a Read node and bind the Tool to the Read’s output.Add a second Read between the existing Extract card details Read and the Form:
  • Name: Resolve card_id
  • Look at: Interaction History + User Attributes
  • Find these details: field card_id, type String, required.
  • Instructions: “Given the user_attribute active_cards (an array of cards with id and last_four) and the tree variable card_last_four: if card_last_four is non-empty and exactly one card in active_cards has a matching last_four, return that card’s id. If card_last_four is empty but active_cards has exactly one entry, return that entry’s id. Otherwise, fail (return empty) so the Fallback below can prompt the customer for clarification.”
Then wrap the Freeze Tool plus everything downstream in a Fallback whose second child is a Reply:
The Read fails when it can’t unambiguously pick a card, the Steps fails, the Fallback advances to the disambiguation Reply, and the customer’s next message goes through the rule again with card_last_four populated. This is the documented “try the primary path; fall back if it doesn’t apply” pattern from the Rulebook reference.Now configure the Freeze Tool itself:
  • Action: select Freeze Card.
  • Inputs:
    • card_id → bind to the card_id tree variable from the Resolve Read.
    • reason → bind to replacement_reason (from the earlier Read).
Once the inputs are bound, the Tool’s outputs (freeze_confirmation_id, frozen_at) become tree variables.
8

Add the Order Replacement Card Tool

Click + Add step. Pick Tool. Configure:
  • Action: select Order Replacement Card.
  • Inputs:
    • account_id → bind to account_id (User Attribute).
    • prior_freeze_confirmation_id → bind to freeze_confirmation_id (upstream Tool output).
    • replacement_reason → bind to replacement_reason.
    • shipping_street, shipping_city, shipping_postal_code, shipping_country → bind each to the matching Form output.
The prior_freeze_confirmation_id binding is the load-bearing link: this Tool cannot fire unless the upstream Freeze Tool succeeded, the dropdown will only show freeze_confirmation_id once the Freeze Tool is in place above this node.
9

Add the Reply node

Click + Add step. Pick Reply. Instructions:
“Confirm to the customer that their old card has been frozen and a replacement is on the way. Mention the new card’s last four digits (lastfour),theexpecteddeliverydate({last_four}), the expected delivery date (), and include the tracking URL ($) so they can monitor delivery. Reassure them that their old card can no longer be used. Suggest they keep an eye out for any unauthorized transactions on the frozen card and reach out if they see any.”
The ${variable} placeholders interpolate from the Order Tool’s outputs. The Reply node passes this as guidance to the agent’s reply-composition pass; the agent writes the final wording in its voice.
Handle null tracking_url gracefully. If your processor doesn’t always return a tracking URL at order time, your Reply instructions should say so: “…include the tracking URL if available ($); if it’s empty, tell the customer the tracking link will arrive in a separate email when the card ships.” The reply-composition pass handles conditional language naturally.
Voice control lives elsewhere. Reply instructions are for content directives (“mention the new card’s last four”, “include the tracking link”). For voice (tone, vocabulary, how to phrase security reassurance), edit your agent’s Main Guidelines → Tone, those rules apply to every reply your agent sends, including this one. For high-stakes flows like fraud-adjacent card replacement, also revisit the Planning Prompt → Escalation Topics to make sure the right edge cases (e.g., “customer mentions identity theft”) escalate to a human before the rule fires.
10

Assign to your agent

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

Test before publishing

Open the ▶ Test Run panel from the rule editor. Start with Custom JSON:
The form_submission object simulates the Form submission, the test panel injects these values as if the customer had filled the form. Click Run Test and walk the trace. A successful run looks like this:
AI Steps panel for a successful card replacement. Sections labeled Planning (collapsed, showing it routed to Card replacement), Executed Rule (expanded), Generate Answer, Input Tag Selection, Output Tag Selection. Inside Executed Rule, seven rows in order: Intent is card lost or stolen (Check, green), Account is active (Check, green), Extract card details (Read, green) showing card_last_four 4242 and replacement_reason stolen, Shipping address (Form, green) showing the four submitted fields, Freeze Card (Tool, green) showing card_id card_42 input and freeze_confirmation_id output, Order Replacement Card (Tool, green) showing prior_freeze_confirmation_id flowing in and new_card_id and expected_delivery outputs, Confirm replacement (Reply, green).
What to verify:
  1. Both top Checks pass.
  2. The Read populates card_last_four = 4242 and replacement_reason = stolen.
  3. The Form node passes (with the injected form_submission values).
  4. The Freeze Tool fires with card_id = card_42 and produces a freeze_confirmation_id.
  5. The Order Tool fires with that confirmation flowing into prior_freeze_confirmation_id, plus all four shipping fields from the Form.
  6. The Reply interpolates new_card_id, expected_delivery, and tracking_url from the Order Tool’s outputs.
If any node shows a red dot, click into it to see what value it had. The most common cause of a red Order Tool is the prior_freeze_confirmation_id binding being missing, the dropdown only populates once the upstream Freeze Tool is correctly configured.
12

Publish

Save commits the draft. Publish promotes it to live.
  • The rule fires on activation messages: your Description’s negative examples weren’t strong enough, or the Topic tag is too broad. Tighten both. Reuse the AI Steps trace’s Planning section to confirm which rule the Planner picked.
  • The Form node doesn’t render: the deploy channel is email-only. See the Channel notes callout above for the Read-based alternative.
  • The Freeze Tool fires but the Order Tool short-circuits: the prior_freeze_confirmation_id binding is missing or the upstream Tool failed. Open the trace and look at the Freeze Tool’s output values; if freeze_confirmation_id is empty, the freeze API itself errored (most often a 404 on the card_id because the card doesn’t belong to that account).
  • The customer’s address in the Form doesn’t match what the processor accepts: US processors generally require five-digit zip codes; international processors accept various formats. The Form Error children should catch obvious mismatches, but processor-side validation may still reject. Pass the error message back to the customer in a Fallback child.
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 fraud gate

You’ve now got a working card-replacement flow. This step adds a safety net: for high-risk customers or customers with repeat replacements in the last 30 days, the agent should still run the full flow (freeze the card, draft the confirmation) but post the confirmation as an internal note instead of replying to the customer directly. A human reviews the case, optionally calls the customer to verify identity, and then either approves the replacement or reverses it before the new card actually ships. This is the canonical fraud-prevention pattern for fintech: don’t refuse to help the customer (refusing makes the agent useless when fraud is most likely), but require human approval before the side effect customers actually feel (the replacement card landing in someone’s mailbox).
1

Open the Internal Comment card

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

Add the first condition group: high-risk customers

In the first condition group, add two predicates:
  • User Attributes → is_high_risk Equals True
  • Tag Groups → Type of Issue Equals Card Lost or Stolen (use Contains instead if your tag group has Tag Selection set to “Multiple tags can be selected”)
Predicates in the same group are AND’d, both must be true.
3

Add the second condition group: repeat replacements

Click + Add alternative condition to create a second group (OR’d with the first). Add:
  • User Attributes → recent_replacement_count Greater Than 1
  • Tag Groups → Type of Issue Equals Card Lost or Stolen
Either group’s matching is enough to gate the reply, fraud-flagged customers OR customers replacing their second-or-later card in 30 days. The three Reply Behavior cards (No Reply, Internal Comment, Direct Reply) evaluate independently; the stricter behavior wins (No Reply > Internal Comment > Direct Reply).
4

Enable and save

Toggle the Internal Comment switch on in the card’s top-right. Click Save.
The Order Tool still fires. Reply Behavior gates only the agent’s final reply (does the customer see it, does a teammate see it, or does nobody see it). It does not gate Tools inside the Rulebook tree. So when this Internal Comment rule fires, the card is still frozen and the replacement is still ordered, the customer just doesn’t see the confirmation. For a true “freeze but hold replacement for human approval” pattern, see the Holding the destructive Action itself note below.
Holding the destructive Action itself. Reply Behavior is the right tool when freezing the card is always safe (it is, freezing is reversible) but the customer-facing confirmation needs human review. If you want to hold the replacement order itself, split the rule into two: an immediate rule that freezes the card and posts an internal note, and a follow-up rule (triggered manually by a human or by a different intent like “I’ve verified the customer”) that calls the Order Action. This is heavier to maintain but matches the operating model some banks require.
  • The card doesn’t enable: there’s a predicate row with no value. Walk through each row and confirm every field, operator, and value is filled in.
  • The Internal Comment fires when it shouldn’t: check whether your recent_replacement_count field is being correctly populated by the Customer Identity attribute, a default-of-null behaves like zero in some processors and like unknown in others. If unsure, add a third predicate: recent_replacement_count Is Not Null AND Greater Than 1.
  • Internal Comment doesn’t fire when it should: a higher-priority No Reply rule may be catching the conversation first. Audit your No Reply rules per Reply Behavior → When multiple cards match. Priority order: No Reply > Internal Comment > Direct Reply.
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 four test messages from a real conversation surface (your widget, primarily, since the Form needs the chat channel). Use test customer accounts whose accounts resolve through your card-management API and whose cards you can safely freeze.
  1. Standard, single-card, with last_four mentioned. “I lost my card ending in 4242, please send a replacement.” Within seconds, the agent freezes the card, asks for the shipping address via the Form, the customer fills it in, the replacement ships, and the customer sees a direct reply confirming the new card’s last four and expected delivery.
  2. Standard, multi-card, no last_four. “My wallet was stolen.” The agent disambiguates by asking which card. The customer replies with “my debit card” or “the one ending in 1881”, the rule re-fires with the new context, the Form renders, and the replacement ships.
  3. High-risk customer. Same as test 1, but the customer’s is_high_risk is true. The Rulebook still runs the full tree, the card is frozen, the order is placed, the reply is drafted, but the reply posts as an internal note instead of going to the customer. A teammate reviews and follows up.
  4. Account closed. Same message, but a customer whose account_status is closed. The account-state Check at the top fails, the rule short-circuits cleanly, and the agent falls through to its default knowledge-base reply. No Tool fires, no card is touched.
Verify in the AI Steps trace. Open each test conversation in the Inbox. For tests 1 and 2, expect a green-dot trace through all seven nodes; for test 3, the same trace plus a Reply Behavior decision routing to Internal Comment; for test 4, the trace stops at the account_status Check with a red dot. The trace is your source of truth in production.

Lock it down with a regression test

Card replacement is one of the highest-stakes flows in fintech support; regress this rule and you ship cards to the wrong people. Add it to your Test Suite immediately after publishing. A minimum useful test set: Bind one of Fini’s six pre-built judges, Tool use correctness is the natural fit for card replacement (did the Freeze and Order Tools fire with the right inputs, in the right order?), pair it with Goal resolution if you want to also grade whether the customer ended up with their stated outcome. Scenarios run as simulated multi-turn conversations, so write expected outcomes that describe the arc, not verbatim wording.
Test Suite runs hit your real APIs. Each scenario replay actually invokes the Tools, which means a Freeze Card + Order Replacement Card run will fire your card-management API for real on the bound test customer. Point the Actions at a sandbox processor while iterating on the test set, or accept that each run produces real freezes and real replacement orders.
After a week in production, open Analytics → Overview and find your card-replacement intent’s row in the Category breakdown table (Analytics doesn’t have a generic tag filter; intent breakdowns are surfaced via the Category breakdown view). Click the row to spot-check conversations. Spikes in the escalated rate usually mean the disambiguation prompt isn’t working or the Form is failing for a country your processor doesn’t ship to; open a sample of those conversations in the Inbox and read the trace.
Knowledge handles the fall-through paths. When the intent Check fails, when account_status Equals active fails, when the disambiguation Reply runs because the Resolve Read couldn’t pick a card, the agent answers from your Knowledge base. For this walkthrough to feel complete to customers, make sure your Articles cover: “We can’t replace cards for closed or suspended accounts”, “What to do if you have a card that’s frozen but not lost”, and the security guidance you’d want a customer to hear if they’re confused about whether they were compromised. Background AI watches conversations and proposes Articles for gaps it detects, check the Review queue weekly.
Identity verification. This walkthrough assumes the customer is already authenticated (the widget is signed with a JWT, or the helpdesk integration carries an authenticated user id). If your channel is unauthenticated (a public form, a chat on a marketing page), insert an identity-verification step before the Freeze Tool, typically a short-lived OTP sent to the customer’s email or phone, with a Read or Form node capturing the OTP and a Check verifying it against your auth service. Don’t let an unauthenticated channel freeze cards; the fraud risk outweighs the convenience.

What to vary

Once the basic flow is in place, common adaptations:
  • Add Apple/Google Wallet provisioning. Chain a third Tool after the Order Action that calls your processor’s provisioning endpoint to issue a virtual card the customer can use immediately on a digital wallet, useful when the customer needs to keep transacting while waiting for the physical replacement.
  • Skip the Form for known-address customers. If Customer Identity exposes a verified billing address, gate the Form behind a Check: billing_address_verified Equals False. When the address is already verified, jump straight from Read to the Tools using the attribute’s address fields.
  • Expedited shipping for VIPs. Add a Check on is_vip between the Form and the Order Tool. If is_vip Equals True, set the Order Tool’s body to include "shipping_speed": "overnight". The processor charges your account the upcharge automatically.
  • Two-card pre-emptive freeze. If the customer says “my wallet was stolen” and has multiple cards, you may want to freeze all active cards (not just the one they identified). The Rulebook doesn’t have a generic iteration node, so the cleanest pattern is to extend your Freeze Card Action server-side to accept either a single card_id or an account_id (freezing all cards on that account), and bind the Tool to account_id when the customer’s wording is “wallet” / “all my cards” / etc., gated by a Check on a freeze_all boolean populated by an upstream Read.
  • Suspicious activity sweep. After ordering the replacement, chain a Lookup Recent Transactions Action that pulls the last 24 hours of activity on the frozen card and includes any flagged transactions in the Reply, so the customer can immediately flag any they didn’t authorize.
  • Localized shipping countries. Pre-populate the Form’s shipping_country dropdown with the customer’s known country first (from Customer Identity), then a sorted full list. Reduces form friction for international customers.
The pattern (Attributes for context → chained Actions for work → Rulebook with Form for orchestration → Reply Behavior for fraud gating) generalizes to most fintech workflows: dispute filing, payment skip / deferral, identity verification re-runs, account closures with grace periods. Anything where you’d write a multi-step runbook for a human fraud or support agent maps cleanly to this five-piece structure.

Troubleshooting

Stages where this walkthrough commonly gets stuck, ordered by likelihood.
The Description isn’t disambiguating well enough. Open three or four such conversations in the Inbox; copy the customer’s actual phrasings into the negative-examples section of the Description (“do not use this rule for: ‘my card isn’t working at the ATM’, ‘I need to set my PIN’, …”). Re-publish and re-test.
Customers sometimes mention amounts that look like card numbers (“a $4242 charge”) or order numbers. Tighten the Read’s instructions: “Only extract a number as card_last_four if the customer explicitly says ‘card’, ‘debit’, ‘credit’, or ‘ending in’. Do not extract amounts in currency or other reference numbers.”
Forms only render in the widget. Salesforce email cases, Zendesk email-only, and similar render as a placeholder. If you need this rule to work cross-channel, build a Read-only variant of the rule (no Form) for email and a Form variant for widget; assign each to the relevant agent.
Two common causes: (1) the Form’s pre-fill is wiring stale customer data instead of the current submission, untoggle pre-fill or remap the Tool inputs to read from the Form’s submission, not the attribute; (2) your processor’s address field names don’t match your Save From Response. Test the Order Action’s Play panel with deliberate values and confirm what the processor actually shipped.
Idempotency isn’t taking. Three things to check: (1) the idempotency_key in the Body is non-empty (some processors silently treat empty as “not idempotent”), (2) the processor reads idempotency from the body field name you used, some want a header (Idempotency-Key) instead, (3) the key is deterministic across retries, ${card_id}-freeze is deterministic; ${now} is not.
Either your is_high_risk attribute isn’t populating, or the predicate has a value mismatch (true vs True, type coercion). Open the conversation in the Inbox, expand AI Steps → Executed User Attributes, and click the Customer Identity row to see the actual returned values. Compare against the predicate.
Most processors allow card order cancellation within a short window (typically minutes to a few hours, before the card enters the production queue). The reversal is an API call on the processor side, not a Fini concept; document the operational runbook for your team. To prevent recurrences, tighten the Form’s validation (postal-code regex per country) and consider adding an “Are you sure?” confirmation Reply between the Form and the Order Tool, the customer sees the full shipping address one last time and types yes to proceed.
Some processors don’t issue a tracking URL until the card physically ships (hours later). Adjust the Reply instructions to handle the null case: “Include the tracking URL if available; otherwise tell the customer the tracking link will arrive in a separate email when the card ships.”

Cancellation flow

The simpler single-Tool walkthrough, refunds, subscription pauses, account closures.

Order status and changes

The e-commerce companion: a Fallback-rooted rule handling three intents in one tree.

Rulebook

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

Actions

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

Attributes

Sources, fields, switches, and the dependency order Fini uses when fetching attributes.

Reply Behavior

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

Test Suite

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

Tags

Configure the Topic tag group the intent Check references.