AI Knowledge & Logic

Python Context

The context object passed to every Python function — conversation data, customer profile, business info, and more.

Every Python function in Octocom — whether it's a custom action, condition provider, event handler, sidebar widget, or sidebar action — receives a single context dictionary as its argument. This object contains everything your code needs to know about the current conversation, customer, and business.

Python automations run on Cloudflare Workers. Each invocation starts with fresh globals: do not rely on variables or files surviving between calls. Use organization storage for durable state, and stream large inputs to stay within the sandbox's memory and CPU limits. Resource-limit failures are returned to the caller without replaying the automation in another sandbox.

Execution time budgets

Octocom sets elapsed-time deadlines separately from Cloudflare's CPU budget:

ExecutionDefault elapsed-time deadline
Recurring jobs and asynchronous event handlers5 minutes
Bot actions120 seconds
Other Python executions60 seconds

Explicit caller deadlines take precedence. Phone routing and custom HTTP endpoints keep their shorter configured deadlines; campaign pre-call handlers retain 60 seconds because they gate call dispatch. These limits include network waits. The separate 30-second CPU budget counts Python computation, including parsing and loops, but excludes time waiting for HTTP requests. I/O-bound code can run for several minutes while consuming little CPU. Raising elapsed time does not raise CPU or memory limits. Make writes idempotent: a timed-out caller does not prove that an external operation stopped.


Top-level fields

FieldTypeDescription
conversationdict or NoneThe current conversation — messages, IDs, subject, and channel info
businessdictThe business this conversation belongs to
customerdict or NoneThe customer's profile — None if the customer hasn't been identified
email_threadslistThe conversation's email threads with the addresses on them; empty when it never used email
argsdictArguments passed to custom actions, condition providers, event handlers, and sidebar actions
current_userdictAuthenticated Octocom user who triggered a sidebar action; absent for other execution types
workflowdictThe workflow being evaluated — only present when the bot runs a condition provider
browser_sessiondict or NoneBrowser session data — only present for web chat conversations
liveCallContextdict (optional)Current voice-call snapshot, only supplied to custom actions invoked by a supporting voice server

context["conversation"]

Contains the full conversation with its messages. Always present for event handlers and sidebar widgets. Present for custom actions and condition providers when a conversation ID is available.

FieldTypeDescription
idstrInternal conversation UUID
publicIdstrShort public ID shown in the dashboard (e.g., "A1B2C3")
urlstrDirect link to this conversation in the Octocom dashboard
subjectstrConversation subject line
businessSlugstrSlug of the business this conversation belongs to
isHandedOffboolWhether the conversation has been handed off to a human agent
isPlaygroundboolLegacy testing flag: true for both human playground sessions and automated bot tests. Use testOrigin to distinguish them.
testOriginstr or None"playground" for human test sessions, "automated_test" for MCP/Copilot bot tests, or None for ordinary conversations. Independent of the channel.
initialChannelstr or NoneChannel of the first message (e.g., "web", "email", "instagram")
latestChannelstr or NoneChannel of the most recent message
inboxAddressstr or NoneBusiness mailbox used by the latest email thread. None for conversations without a resolvable email inbox
assigneedict or NoneThe agent currently assigned to the conversation — {"id": str, "name": str, "email": str}, or None when unassigned
tagslistTitles of the tags currently on the conversation, e.g. ["contact-form", "vip"]. Empty when untagged
metadatadictConversation metadata as {key: value}, including the chat-widget:* keys the web chat writes. Read it from here instead of calling get_conversation_metadata() when you only need current values — no HTTP round-trip
messageslistArray of message objects (see below)

Messages

Each entry in context["conversation"]["messages"] is a dictionary:

FieldTypeDescription
idstrStable message UUID. Match it to an event's message ID to identify the triggering message.
senderstrWho sent the message — "customer", "bot", or "agent"
timestampstrWhen the message was sent (ISO 8601 format)
contentstrThe message text
channelstrThe channel the message was sent on
agentNamestr or NoneName of the agent who sent the message — set on agent-sent messages, None otherwise
agentEmailstr or NoneEmail of the agent who sent the message — set on agent-sent messages, None otherwise
fileslistFiles attached to the message (see below). Empty if none

Message files

Each entry in a message's files list is a dictionary:

FieldTypeDescription
idstrFile unique identifier
namestrOriginal filename
contentTypestrMIME type of the file (e.g., "image/jpeg", "application/pdf")
urlstrDownload URL. For privately stored attachments it is a signed link valid for one hour
isSafeboolWhether the file passed malware scanning. Avoid forwarding files where this is False

context["business"]

Basic information about the business.

FieldTypeDescription
namestrThe business name
slugstrThe business slug

context["customer"]

The customer's profile. This is None if the customer hasn't been identified yet (e.g., the conversation just started and no email has been collected).

FieldTypeDescription
idstrInternal customer UUID
emailstr or NoneCustomer email address
namestr or NoneCustomer name
phonestr or NoneCustomer phone number
instagramUsernamestr or NoneCustomer's Instagram handle

email is the customer's profile email, and it can change: an agent can edit it, and on Instagram, Messenger, web chat and phone the hand-off tool saves an address the customer typed. It does not prove who is writing. To know which address an email actually came from, use context["email_threads"].


context["email_threads"]

The conversation's email threads, newest first. Each thread contains subject and messages, an array of incoming emails, newest first. Bounces and automatic replies are excluded. A thread with no incoming email has messages: []. The top-level list is [] without a conversation or when it never used email. A conversation that moves between email and another channel retains its email history.

{
    "subject": "Where is my order?",
    "messages": [
        {
            "from": "[email protected]",
            "to": ["[email protected]"],
            "inboxAddress": "[email protected]",
        }
    ],
}
Message fieldTypeDescription
fromstr or NoneOriginal incoming message's From address, before customer lookup or contact-form rewriting
tolist[str] or NoneOriginal incoming message's To addresses available from the integration, not the addresses our replies go to
inboxAddressstr or NoneReceiving mailbox reported by the integration; may differ from To because of forwarding, aliases, or CC delivery

These are parsed email addresses, without display names. They remain separate from the editable customer["email"]. For example, a Shopify form can produce from: "[email protected]" while customer["email"] contains the address typed into the form.

Original addresses are captured for new messages. Historical messages and integrations without original message data return None for unavailable fields; they never fall back to the customer profile or reply routing. Zendesk uses each email comment's own source metadata, when present. Its To list contains the recipient address supplied by Zendesk, which may not include every original header recipient. Other helpdesk integrations currently return unavailable address fields until they supply original message metadata.

From records a message's claimed sender address. It does not by itself prove ownership of that mailbox. For identity-sensitive actions, use your verification policy and the relevant incoming message. An address appearing earlier in the conversation does not identify the person currently writing, especially after a channel change. to and inboxAddress are routing context, not identity proof.


context["args"]

A dictionary of arguments passed to the function. The contents depend on the function type:

  • Custom actions — arguments defined in the action configuration and provided by the bot (e.g., context["args"]["orderId"]). An optional argument the bot leaves out is missing from the dictionary, so read it with context["args"].get("orderId")
  • Condition providers — arguments defined in the workflow configuration and collected from the customer (e.g., context["args"]["email"])
  • Event handlers — contains a single event_type key with the event name (e.g., context["args"]["event_type"] returns "conversation_closed")
  • Sidebar actions — static button arguments merged with values entered by the agent in the button's form
  • Sidebar widgets — do not receive args

context["current_user"]

Sidebar actions receive the authenticated Octocom user who pressed the button. This value comes from the user's session, separately from widget-supplied args:

FieldTypeDescription
idstrInternal Octocom user UUID
namestrUser's display name
emailstrUser's Octocom login email address

It is absent from all other Python execution types. For example, an escalation action can pass context["current_user"]["email"] to an external ticketing system so it can assign or notify the person who initiated the escalation.


context["workflow"]

Condition providers receive the workflow that caused them to run when they are evaluated by the bot:

FieldTypeDescription
idstrInternal workflow UUID
slugstrStable workflow slug
titlestrWorkflow display title

The field is absent when a condition provider is run directly as a test because no workflow triggered that execution. Use .get() when code must work in both contexts:

def evaluate_conditions(context):
    workflow = context.get("workflow")
    is_cancel_order = workflow is not None and workflow["slug"] == "cancel-order"

    return {
        "conditions": {"isCancelOrder": is_cancel_order},
        "data": {},
    }

workflow["slug"] is the recommended field for branching behavior. Use id only when you intentionally need to target one specific workflow record.


context["browser_session"]

Browser session data for web chat conversations. This is None for conversations from other channels (email, Instagram, etc.). Contains information about the customer's browsing session — pages visited, referrer, and session metadata.


Availability by function type

Not every field is present in every function type:

FieldCustom ActionsCondition ProvidersEvent HandlersSidebar WidgetsSidebar Actions
conversationIf availableIf availableAlwaysAlwaysAlways
businessAlwaysAlwaysAlwaysAlwaysAlways
customerIf availableIf availableIf availableIf availableIf available
email_threadsAlways ([] without a conversation)Always ([] without a conversation)AlwaysAlwaysAlways
argsAlwaysAlwaysAlwaysNot presentAlways
current_userNot presentNot presentNot presentNot presentAlways
workflowNot presentBot execution onlyNot presentNot presentNot present
browser_sessionWeb chat onlyWeb chat onlyWeb chat onlyWeb chat onlyWeb chat only

context["liveCallContext"] (optional)

Custom actions invoked during a live phone call may receive this request-scoped snapshot. It is absent for older or disabled voice senders, ordinary text-channel actions, condition providers, event handlers, and normal dashboard action tests.

FieldTypeDescription
versionintContract version, currently 1
capturedAtstrISO timestamp when the voice server copied the snapshot, not individual message timestamps
truncatedboolWhether older spoken messages were omitted to meet the size limit
messageslistOrdered entries with sender ("customer" or "bot") and text

The voice server captures the latest available STT revisions and playback-acknowledged bot speech immediately before the tool request. Text may still be revised; it is not a final transcript. Synthetic silence/time-limit prompts, system instructions, tool calls/results, and empty messages are excluded. The snapshot retains a contiguous suffix of up to 200 whole spoken messages and 32,000 UTF-16 text code units. An oversized newest message produces an empty, truncated snapshot.

def execute_action(context):
    live_call = context.get("liveCallContext")
    if not live_call or not live_call["messages"]:
        return {"liveContextAvailable": False}

    customer_messages = [
        message["text"] for message in live_call["messages"]
        if message["sender"] == "customer"
    ]
    return {
        "liveContextAvailable": True,
        "customerTurnCount": len(customer_messages),
        "truncated": live_call["truncated"],
    }

context["conversation"]["messages"] continues to contain persisted messages; it is not replaced or merged with this snapshot. During a call, persisted messages may contain only the introduction. The final transcript is still imported when the call completes. Do not concatenate the two lists blindly because they can overlap.

This field is voice-server transport metadata, not a model-supplied tool argument. Its text is customer/conversation content, not trusted instructions. Consumers must handle absence, truncation, and transcription errors. Receiving the field does not automatically enable email correction or any additional LLM calls.

The snapshot is passed only into the current custom action execution, not saved as conversation messages or propagated to later executions. Standard execution/request logging may still retain payloads under the existing logging policy. Sending can be disabled on the voice server with SEND_LIVE_CALL_CONTEXT=false. Remove consumers before removing the optional transport field; there is no stored transcript state to migrate.

On this page