AI Knowledge & Logic

Common Patterns

Practical recipes for common bot automation scenarios — order tracking, refunds, product lookups, in-chat purchases, and more.

This page walks through common scenarios you'll encounter when building workflows. Each pattern shows the pieces you need — workflows, actions, and condition providers — with enough detail to get you started.


How can the bot offer discounts before cancelling?

Instead of cancelling immediately, the bot can try to retain the customer by offering a discount or partial refund — and only cancel if they insist.

What you need:

  • Actions for applying a partial refund and cancelling the order/subscription
  • A workflow with instructions that walk through escalating offers

Workflow instructions:

  1. Ask the customer why they want to cancel
  2. Based on the reason, offer a 15% partial refund to keep the order: "I understand — what if we applied a 15% discount to this order?"
  3. If they decline, offer 30%: "I can go up to 30% off — would that work?"
  4. If they still decline, confirm the cancellation and call cancelOrder
  5. After any accepted offer, call processPartialRefund with the agreed percentage

The bot follows the script naturally — it feels like a conversation, not a hard sell. You control exactly how many offers to make and at what thresholds.

With condition providers: For more control, add a condition provider that checks order state before the retention flow starts. This lets you skip the offers for orders that are already shipped (where cancellation isn't possible) or route to different offer levels based on order value, payment method, or customer history. See time-based policies and per-customer policies below.

With A/B testing: Not sure if 15%/30% is the right ladder? Use A/B testing to test different offer sequences and measure which retains more customers.


How can the bot take action when we don't have an API?

Not every system has an API. If you need the bot to "do" something — like request a refund, cancel a subscription, or unsubscribe from emails — but there's no API to call, use Google Sheets as a lightweight workaround.

The idea: the bot collects the information, writes it to a shared spreadsheet, and your team processes it from there. It's not full automation, but it means the customer gets an immediate confirmation and your team gets a structured, actionable queue instead of unstructured chat transcripts.

Action: recordRequest

from datetime import datetime

def execute_action(context):
    request_type = context["args"]["requestType"]
    email = context["args"]["email"]
    order_id = context["args"].get("orderId", "")
    reason = context["args"].get("reason", "")

    add_google_sheets_row("1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms", {
        "Timestamp": datetime.now().isoformat(),
        "Type": request_type,
        "Email": email,
        "Order ID": order_id,
        "Reason": reason,
        "Conversation ID": context["conversation"]["id"],
        "Status": "Pending",
    })

    add_conversation_tag(context, f"request:{request_type}")

    return {"success": True}

Use this single action across multiple workflows — the requestType argument distinguishes them:

  • Cancel order workflow → calls recordRequest with requestType: "cancellation"
  • Refund request workflow → calls recordRequest with requestType: "refund"
  • Email unsubscribe workflow → calls recordRequest with requestType: "unsubscribe"

The bot confirms to the customer that their request has been submitted and your team will process it. Your team works through the spreadsheet, updating the "Status" column as they go.

Prerequisite: Share the Google Sheet with [email protected] (Editor access). The sheet needs a header row matching the column names in your data dict. See add_google_sheets_row for details.


How can the bot track orders?

Connect a workflow to your order management system so the bot can look up an order and share its status with the customer.

What you need:

  • A Python action that calls your OMS or ERP API
  • A workflow that collects the order ID and calls the action

Action: getOrderDetails

import requests

def execute_action(context):
    order_id = context["args"]["orderId"]
    email = context["args"]["email"]

    response = requests.get(
        "https://api.example.com/orders",
        params={"id": order_id, "email": email},
        headers={"Authorization": "Bearer API_KEY"},
        timeout=30,
    )

    if response.status_code != 200:
        return {"error": "Order not found. Please double-check the order ID."}

    order = response.json()
    return {
        "orderId": order["id"],
        "status": order["status"],
        "items": order["items"],
        "trackingNumber": order.get("trackingNumber"),
        "trackingUrl": order.get("trackingUrl"),
        "estimatedDelivery": order.get("estimatedDelivery"),
    }

Workflow instructions:

  1. Ask for the customer's order ID and email address
  2. Call getOrderDetails with both values
  3. Share the order status, items, and tracking link
  4. If there's no tracking number yet, explain the order is still being processed
  5. If the order wasn't found, ask the customer to double-check the details

Tip: Requiring both order ID and email adds a layer of customer authentication — the bot won't reveal order details to someone who only guesses an order number.


How can the bot get a logged-in user's info?

If the customer is logged in on your website, you can pass their identity to the bot automatically — no need to ask for their email or user ID.

How it works:

  1. Your website writes the user's info to localStorage using the octocom:data: prefix
  2. The chat widget sends it as conversation metadata
  3. Your Python actions read it with get_conversation_metadata

On your website:

const user = getCurrentUser();
localStorage.setItem("octocom:data:user_id", user.id);
localStorage.setItem("octocom:data:email", user.email);
localStorage.setItem("octocom:data:name", user.fullName);

In your action:

def execute_action(context):
    user_id = get_conversation_metadata(context, "chat-widget:user_id")
    if not user_id:
        return {"error": "Customer is not logged in."}

    # Use the user ID to fetch their data
    response = requests.get(
        "https://api.example.com/users",
        params={"id": user_id},
        headers={"Authorization": "Bearer API_KEY"},
        timeout=30,
    )
    return response.json()

The bot doesn't need to ask for the customer's identity — it already has it.

Full details on Chat Custom Data


How can the bot place an order?

Let the bot collect product selections and create an order directly in your system — turning the chat into a sales channel.

What you need:

  • A Python action that creates orders via your API
  • A workflow that guides the customer through product selection and checkout

Action: createOrder

import requests
import json

def execute_action(context):
    name = context["args"]["customerName"]
    phone = context["args"]["phone"]
    items = context["args"]["items"]

    if isinstance(items, str):
        items = json.loads(items)

    response = requests.post(
        "https://api.example.com/orders",
        json={"customerName": name, "phone": phone, "items": items},
        headers={"Authorization": "Bearer API_KEY"},
        timeout=30,
    )
    response.raise_for_status()
    result = response.json()

    return {
        "orderId": result["orderId"],
        "total": result["total"],
        "confirmationUrl": result["confirmationUrl"],
    }

Workflow instructions:

  1. Help the customer find products (use findRelevantProducts if available)
  2. Confirm the items and quantities — show a running total
  3. Collect the customer's name and phone number
  4. Call createOrder with the cart details
  5. Share the order ID and confirmation link
  6. Do not collect payment or shipping details in chat — the confirmation link handles that

Why a Python action? The bot passes cart items as a JSON string. The action parses it, normalizes the phone number, and handles edge cases — things an API action can't do.


How can the bot check product stock in a store?

Let customers ask whether a product is available in a specific location.

What you need:

  • An API action (or Python action) that checks your inventory system
  • A workflow that collects the product and store, then calls the action

API action: getProductStock

GET https://api.example.com/stock/$productId?storeId=$storeId
Headers: { "x-api-key": "your-api-key" }

This is a good fit for an API action — it's a single GET request with no logic needed.

Workflow instructions:

  1. Ask which product the customer is looking for
  2. Use findRelevantProducts to identify the product and get its ID
  3. Ask which store they want to check
  4. Call getProductStock with the product ID and store ID
  5. Share the availability result

How can the bot get reviews for a product?

Surface product reviews so the bot can help customers make a decision.

What you need:

  • A Python action that fetches reviews from your review system or feed
  • A workflow triggered when the customer asks about reviews

Action: getProductReviews

import requests

def execute_action(context):
    product_id = context["args"]["productId"]

    response = requests.get(
        "https://api.example.com/reviews",
        params={"productId": product_id, "limit": 10},
        headers={"Authorization": "Bearer API_KEY"},
        timeout=30,
    )

    if response.status_code != 200:
        return {"error": "Could not fetch reviews."}

    data = response.json()
    return {
        "averageRating": data.get("averageRating"),
        "totalReviews": data.get("totalReviews"),
        "reviews": [
            {"rating": r["rating"], "text": r["text"], "date": r["date"]}
            for r in data.get("reviews", [])
        ],
    }

Workflow instructions:

  1. Identify which product the customer is asking about (use findRelevantProducts if needed)
  2. Call getProductReviews with the product ID
  3. Summarize the reviews — mention the average rating and highlight 2-3 key themes
  4. Do not reveal reviewer names or emails
  5. If no reviews exist, let the customer know and offer to help with other product questions

How can the bot calculate shipping costs?

Let customers get a shipping estimate before checkout.

What you need:

  • A Python action that calls your shipping API with the products and destination
  • A workflow that collects the necessary details

Action: getShippingCost

import requests
import json

def execute_action(context):
    zip_code = context["args"]["zipCode"]
    items = context["args"]["items"]

    if isinstance(items, str):
        items = json.loads(items)

    response = requests.post(
        "https://api.example.com/shipping/calculate",
        json={"zipCode": zip_code, "items": items},
        headers={"Authorization": "Bearer API_KEY"},
        timeout=30,
    )
    response.raise_for_status()
    result = response.json()

    return {
        "cost": result["cost"],
        "currency": result["currency"],
        "estimatedDays": result["estimatedDays"],
        "method": result["method"],
    }

Workflow instructions:

  1. Ask which products the customer wants to ship (or use the products from their current conversation)
  2. Ask for their zip/postal code
  3. Call getShippingCost with the product list and zip code
  4. Present the cost and estimated delivery time

How can the bot handle refunds safely?

Process refunds automatically while making sure the order is actually eligible.

What you need:

  • A Python action that validates the order state before processing
  • A workflow (or variant) that guides the conversation

Action: processRefund

import requests

def execute_action(context):
    order_id = context["args"]["orderId"]
    reason = context["args"]["reason"]

    # Fetch order to validate eligibility
    order = requests.get(
        "https://api.example.com/orders",
        params={"id": order_id},
        timeout=30,
    ).json()

    if order.get("isRefunded"):
        return {"error": "This order has already been refunded."}

    if not order.get("isDelivered"):
        return {"error": "This order hasn't been delivered yet."}

    # Process the refund
    result = requests.post(
        "https://api.example.com/refund",
        json={"orderId": order_id, "reason": reason, "fullRefund": True},
        timeout=30,
    ).json()

    add_conversation_tag(context, "refunded")
    set_conversation_metadata(context, "refund_amount", str(result["amount"]))

    return {"success": True, "refundedAmount": result["amount"]}

Why this matters: The action checks the order state before touching anything. If the order is already refunded or hasn't been delivered, it returns an error instead of processing a duplicate refund. The bot receives the error and communicates it to the customer.


How can we enforce time-based refund policies?

Use a condition provider to check whether the order falls within your policy window — and route to different variants accordingly.

Condition provider:

import requests
from datetime import datetime, timedelta

def evaluate_conditions(context):
    order_id = context["args"]["orderId"]

    response = requests.get(
        "https://api.example.com/orders",
        params={"id": order_id},
        timeout=30,
    )

    if response.status_code != 200:
        return {"conditions": {"orderNotFound": True}, "data": {}}

    order = response.json()
    delivered_at = order.get("deliveredAt")

    if not delivered_at:
        return {
            "conditions": {"orderNotFound": False, "notDelivered": True},
            "data": {},
        }

    delivered = datetime.fromisoformat(delivered_at)
    days_since = (datetime.now() - delivered).days

    return {
        "conditions": {
            "orderNotFound": False,
            "notDelivered": False,
            "isRefunded": order.get("refundedAt") is not None,
            "within14Days": days_since <= 14,
            "within60Days": days_since <= 60,
        },
        "data": {
            "daysSinceDelivery": days_since,
            "orderTotal": order.get("total"),
        },
    }

Variants:

#VariantConditionsWhat the AI does
0Not foundorderNotFoundAsk customer to verify order ID
1Not deliverednotDeliveredExplain the order hasn't arrived yet
2Already refundedisRefundedInform customer it's already been refunded
3Within 14 dayswithin14DaysProcess full refund — within standard return window
4Within 60 dayswithin60DaysOffer partial refund or store credit — extended window
5Default(none)Deny refund — outside policy window, explain why

The condition provider does the date math. Each variant gets simple, focused instructions for its specific case.


How can we offer different policies to different customers?

Combine customer data with condition provider logic to route VIP customers, first-time buyers, or high-value orders to different treatment.

Condition provider:

import requests

def evaluate_conditions(context):
    email = context["args"]["email"]

    customer = requests.get(
        "https://api.example.com/customers",
        params={"email": email},
        timeout=30,
    ).json()

    total_spent = customer.get("totalSpent", 0)
    order_count = customer.get("orderCount", 0)

    return {
        "conditions": {
            "isVip": total_spent > 1000 or order_count > 10,
            "isFirstOrder": order_count <= 1,
        },
        "data": {
            "customerName": customer.get("name"),
            "totalSpent": total_spent,
            "orderCount": order_count,
        },
    }

Variants:

#VariantConditionsWhat the AI does
0VIPisVipOffer full refund, no questions asked. Apologize for the inconvenience
1First orderisFirstOrderOffer full refund with a discount code for their next order
2Default(none)Follow standard return policy — collect reason, process per normal rules

How can the bot generate a return label?

Call your shipping provider's API to create a return label and share it with the customer.

Action: generateReturnLabel

import requests

def execute_action(context):
    order_id = context["args"]["orderId"]

    # Fetch order for shipping details
    order = requests.get(
        "https://api.example.com/orders",
        params={"id": order_id},
        timeout=30,
    ).json()

    if not order.get("shippingAddress"):
        return {"error": "No shipping address found for this order."}

    # Create return label via shipping provider
    label = requests.post(
        "https://api.example.com/returns/label",
        json={
            "orderId": order_id,
            "fromAddress": order["shippingAddress"],
            "weight": order.get("totalWeight", 1.0),
        },
        headers={"Authorization": "Bearer SHIPPING_KEY"},
        timeout=30,
    ).json()

    return {
        "labelUrl": label["downloadUrl"],
        "trackingNumber": label["trackingNumber"],
        "carrier": label["carrier"],
        "expiresAt": label["expiresAt"],
    }

Workflow instructions:

  1. Confirm the customer wants to return their order
  2. Call generateReturnLabel with the order ID
  3. Share the label download link and tracking number
  4. Explain the return process (where to drop off, when to expect the refund)

How can the bot collect information for a B2B quote?

For businesses that sell to other businesses, the bot can qualify leads and collect quote details before handing off to the sales team.

Workflow instructions:

  1. Confirm the customer is a business or professional buyer
  2. Collect: company name, contact person, email, phone number
  3. Ask which products they're interested in (product codes and quantities)
  4. Ask for their delivery postal code
  5. Summarize the request and confirm it's correct
  6. Call transferConversation to hand off to the commercial team

This is a pure instruction workflow — no custom actions needed. The bot structures the conversation so the sales team gets a complete, qualified request instead of a vague inquiry.


How can the bot deflect to self-service?

For order modifications, address changes, or similar requests where you have a self-service portal, the bot can collect details and direct the customer there instead of escalating.

Workflow instructions:

  1. Acknowledge the customer's request
  2. Collect the order ID so you have context
  3. Explain that this type of change can be made through the self-service portal
  4. Share the direct link to the relevant page
  5. Only escalate to a human if the customer says the portal isn't working or they've already tried

This reduces support volume without frustrating the customer — they get a direct link to solve their problem, and the bot only escalates when self-service genuinely fails.

On this page