Ai knowledge and logicHelpers

send_bot_message

Send a fixed bot message through an existing conversation.

send_bot_message(
    context: dict,
    text: str,
    idempotency_key: str,
    conversation_id: str | None = None,
) -> dict

Immediately sends customer-visible text through an existing conversation's latest channel. The message is recorded as a bot-authored template message in the same conversation; it does not create a separate email thread and does not trigger an AI response.

This helper is available in recurring jobs, event handlers, and sidebar actions. It is intentionally unavailable in condition providers, system-prompt sections, sidebar widgets, and bot actions, where sending or closing while the bot is still deciding its response can create duplicate or contradictory replies.


Parameters

NameTypeRequiredDescription
contextdictYesThe Python execution context
textstrYesCustomer-visible text, from 1 to 5,000 characters
idempotency_keystrYesA stable, unique key for this logical message, from 1 to 200 characters
conversation_idstr or NoneNoConversation UUID or public ID. Defaults to the current conversation; required in recurring jobs because they have no conversation context

Returns

{
    "success": True,
    "conversationId": "conversation-uuid",
    "messageId": "message-uuid",
    "duplicate": False,
}

duplicate is True when the same idempotency key already delivered the same text to the same conversation. Reusing a key with different text or another conversation raises an HTTP conflict instead of sending anything.


Safeguards

  • The conversation must belong to the organization running the Python code.
  • The conversation must still be open, bot-owned, non-imported, and outside the phone-call channel.
  • The transcript must not change between validation and message creation. If it does, the call fails so the automation can reevaluate the latest customer message.
  • Every message requires an idempotency key. Retries after a completed delivery return the original message instead of sending another copy.
  • If delivery has an ambiguous failure, Octocom does not automatically resend. This avoids showing the customer a duplicate if the channel accepted the first attempt before timing out.
  • Automation bot messages are limited to 100 per organization per minute, in addition to the shared REST API limit.
  • A recurring job test executes the helper normally and sends a real message. Keep a dry_run guard enabled while testing customer-facing jobs.

Recurring job example

Use a key derived from the durable business operation, not from the job run time. That keeps retries and overlapping runs safe.

def run_job(context):
    item = organization_store_get("order-cancellations", "order-123")
    if not item:
        return {"processed": False}

    cancellation = item["value"]
    conversation_id = cancellation["conversationId"]
    order_id = cancellation["orderId"]

    # Perform and verify the refund, package cancellation, and any required
    # subscription cancellation before telling the customer it succeeded.
    result = send_bot_message(
        context,
        f"Your order {order_id} has been cancelled and fully refunded. "
        "The refund will return to your original payment method.",
        idempotency_key=f"order-cancelled:{order_id}:confirmation:v1",
        conversation_id=conversation_id,
    )

    close_conversation(
        context,
        reason=f"Order {order_id} automatically cancelled and customer notified",
        conversation_id=conversation_id,
    )
    return {"processed": True, "messageId": result["messageId"]}

Do not close the conversation when the refund, package cancellation, subscription cancellation, or confirmation delivery fails. Leave it open for retry or manual follow-up.

Event handler example

Event handlers already have a current conversation, so conversation_id can be omitted:

def handle_event(context):
    return send_bot_message(
        context,
        "We completed your requested account update.",
        idempotency_key=f"account-update:{context['conversation']['id']}:v1",
    )

On this page