Skip to main content
Using a Skills-compatible agent like Claude Code? The Greenflash agent skill handles this automatically. Run /greenflash-onboard-agentic instead of copying this prompt.

Upgrade Greenflash Messages API Integration for Agentic Products

You are a coding agent tasked with improving and extending our existing integration with the Greenflash Messages API. We already have a working implementation that logs interactions from our AI agent into Greenflash. Your goal is to advance this implementation so it fully supports agentic workflows, richer message semantics, and better downstream analysis and UI visibility, unlocking significantly more value from Greenflash.

High-Level Objective

Refactor the current implementation so that:
  • User-facing content is clearly separated from internal agent execution details
  • Agent reasoning, tool usage, and workflows are represented using explicit message types
  • Greenflash can accurately analyze, visualize, and optimize complex agent behavior over time
Reference: The full documentation for the Messages API is available at: https://docs.greenflash.ai/api-reference/interactions/capture-interactions-and-messages

Core Concepts to Apply

1. Message Types

Instead of passing everything as plain text messages, you must use the appropriate message_type to describe what the agent is doing. The available message_type values are:
TypeDescription
user_messageInput from the end user
assistant_messageResponse from the assistant
system_messageSystem-level instructions
thoughtInternal reasoning or planning
tool_callInvocation of an external tool (requires tool_name)
observationResult returned from a tool
final_responseThe final user-facing response
retrievalRAG or document retrieval
memory_readReading from agent memory
memory_writeWriting to agent memory
chain_startStart of a multi-step chain
chain_endEnd of a multi-step chain
embeddingEmbedding operation
tool_errorError from a tool
callbackCallback event
llmLLM call
taskTask execution
workflowWorkflow execution
Use these types intentionally. They are how Greenflash understands agent structure, not just conversation flow.

2. content vs output (Very Important)

content
  • Use only for user-facing input or output
  • Anything placed in content will run through all Greenflash analyses
  • Examples:
    • User input
    • Assistant responses that are shown directly to the user
  • If it is not meant to be seen by a user, it should not go here
output
  • Use for internal agent details you still want visible in the Greenflash UI
  • Examples:
    • Thoughts or planning steps
    • Tool call parameters
    • Tool results
    • Internal decisions
  • These are valuable for agentic analysis and debugging, but should not be treated as user-visible language
Rule of thumb: If it’s not directly user-facing, use output, not content.

3. Modeling Agent Execution Properly

You must identify and separate:
WhatMessage TypeField
User inputsuser_messagecontent
Assistant replies shown to usersassistant_message or final_responsecontent
Internal reasoningthoughtoutput
Tool invocationstool_calltool_name (required), input
Tool resultsobservationoutput
Errorstool_erroroutput
Multi-step flowschain_start, chain_end, task, or workflowas appropriate
This structured breakdown is what allows Greenflash to:
  • Visualize agent execution
  • Attribute failures correctly
  • Run higher-quality analyses
  • Power autonomous optimization

4. Message Relationships (Threading)

When messages are causally related (for example, a tool result following a tool call), use:
  • external_message_id on each message
  • parent_external_message_id to link child messages to their parent
This preserves execution order and dependency chains in the Greenflash UI and analytics layer.

5. Automatic De-duplication

Messages with an external_message_id are automatically deduplicated within a conversation. If you resend a batch of messages that includes previously ingested messages (identified by their external_message_id), the existing messages will be skipped and only new ones will be inserted. This makes it safe to send the full conversation history on each request rather than tracking which messages have already been sent.

Submitting Messages

There are two valid submission patterns. You must choose the best one based on how the existing code is structured.

Option A: Batch Submission (All Messages at Once)

Use this when:
  • All messages are available at the end of execution
  • The agent run is fully deterministic and non-streaming
In this case, collect messages into an array and send them in a single request.
Tip: You can also pass model at the top level of messages.create() to track which AI model was used. This enables model performance comparison in Greenflash.
Sync:
client.messages.create(
    product_id="YOUR_PRODUCT_ID",
    external_user_id="USER_ID",
    external_conversation_id="CONVO_ID",
    messages=[
        # User query
        {
            "role": "user",
            "content": "I got charged twice for my subscription this month.",
        },
        # Agent reasoning (internal, not user-facing)
        {
            "external_message_id": "thought-account-lookup",
            "message_type": "thought",
            "output": "Customer reports double charge. Need to verify identity and pull account details to understand the situation.",
        },
        # Tool invocation
        {
            "external_message_id": "tool-account-details",
            "message_type": "tool_call",
            "tool_name": "account_details",
            "input": {
                "search_query": "Michael Chen",
                "include_billing_history": True,
            },
        },
        # Tool result
        {
            "external_message_id": "tool-account-details-result",
            "message_type": "observation",
            "parent_external_message_id": "tool-account-details",
            "output": {
                "recent_charges": [
                    {"date": "2024-01-01", "amount": 49.99, "status": "completed"},
                    {"date": "2024-01-01", "amount": 49.99, "flag": "potential_duplicate"},
                ],
            },
        },
        # Final response (user-facing)
        {
            "role": "assistant",
            "content": "I found your account and can see you were charged $49.99 twice on January 1st. Let me process your refund right now.",
        },
    ],
)
Async (fire-and-forget):
asyncio.create_task(client.messages.create(
    product_id="YOUR_PRODUCT_ID",
    external_user_id="USER_ID",
    external_conversation_id="CONVO_ID",
    messages=[...],  # Same message array as above
))

Option B: Incremental Submission (Messages Over Time)

Use this when:
  • Messages are produced as the agent runs
  • Tool calls, observations, or thoughts occur asynchronously or via streaming
In this case, send messages individually as they happen. Sync example:
# 1. User message arrives
client.messages.create(
    product_id="YOUR_PRODUCT_ID",
    external_user_id="USER_ID",
    external_conversation_id="CONVO_ID",
    messages=[{
        "role": "user",
        "content": "I got charged twice for my subscription this month.",
    }],
)

# 2. Agent begins reasoning (internal)
client.messages.create(
    product_id="YOUR_PRODUCT_ID",
    external_user_id="USER_ID",
    external_conversation_id="CONVO_ID",
    messages=[{
        "external_message_id": "thought-account-lookup",
        "message_type": "thought",
        "output": "Customer reports double charge. Need to pull account details.",
    }],
)

# 3. Agent invokes tool
client.messages.create(
    product_id="YOUR_PRODUCT_ID",
    external_user_id="USER_ID",
    external_conversation_id="CONVO_ID",
    messages=[{
        "external_message_id": "tool-account-details",
        "message_type": "tool_call",
        "tool_name": "account_details",
        "input": {"search_query": "Michael Chen", "include_billing_history": True},
    }],
)

# 4. Tool returns result
client.messages.create(
    product_id="YOUR_PRODUCT_ID",
    external_user_id="USER_ID",
    external_conversation_id="CONVO_ID",
    messages=[{
        "external_message_id": "tool-account-details-result",
        "message_type": "observation",
        "parent_external_message_id": "tool-account-details",
        "output": {"duplicate_charge_confirmed": True, "refund_eligible": True},
    }],
)

# 5. Agent responds to user (user-facing)
client.messages.create(
    product_id="YOUR_PRODUCT_ID",
    external_user_id="USER_ID",
    external_conversation_id="CONVO_ID",
    messages=[{
        "role": "assistant",
        "content": "I found the duplicate charge. Processing your refund now.",
    }],
)
Async (fire-and-forget) example:
# Each call wrapped in asyncio.create_task() for non-blocking logging
asyncio.create_task(client.messages.create(
    product_id="YOUR_PRODUCT_ID",
    external_user_id="USER_ID",
    external_conversation_id="CONVO_ID",
    messages=[{
        "external_message_id": "thought-account-lookup",
        "message_type": "thought",
        "output": "Customer reports double charge. Need to pull account details.",
    }],
))
Both approaches are correct. Your job is to adapt to the existing codebase, not force a pattern.

Async vs Sync Considerations

When updating our integration, match the existing async/sync pattern:
  • Sync apps: Call client.messages.create(...) directly. Use the sync Greenflash client.
  • Async apps: Use AsyncGreenflash and wrap calls in asyncio.create_task() for fire-and-forget logging that won’t block agent execution.
Important: In Python, calling an async function without await returns a coroutine that never executes. Always use asyncio.create_task() for fire-and-forget patterns in async code.

Python SDK Requirements

We are already using the Greenflash Python SDK. You must:
  • Use the Python SDK syntax
  • Call client.messages.create(...)
  • Do not make raw HTTP POST requests
# Correct: Use the SDK
client.messages.create(
    product_id=product_id,
    external_user_id=user_id,
    external_conversation_id=convo_id,
    messages=[{
        "message_type": "tool_call",
        "input": {...},
        "external_message_id": "tool-call-1"
    }],
)
Reference: Follow the SDK conventions shown in the official docs: https://docs.greenflash.ai/sdks/python

Backward-Compatible Refactoring

We may currently be doing something like:
  • Logging tool calls, thoughts, and outputs as plain assistant messages
  • Stuffing everything into content
You should:
  • Preserve the original behavior semantically
  • Translate it into structured messages using the appropriate message_type
  • Move internal data from content to output
  • Avoid breaking existing flows or assumptions

What You Must Deliver

  1. Updated implementation using structured Greenflash messages
  2. Clear separation between:
    • User-visible content (content)
    • Internal agent data (output, input)
  3. Correct usage of message types for all agent activities
  4. Python SDK-based message submission
  5. A submission strategy (batch vs incremental) that fits the existing code