> ## Documentation Index
> Fetch the complete documentation index at: https://docs.greenflash.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agentic Messages (Python)

> System prompt for coding agents to upgrade an existing Python SDK integration to support agentic workflows with structured message types. Copy and paste the content of this page into your coding assistant.

export const CopyAllButton = ({content, label = 'Copy Page'}) => {
  const [copied, setCopied] = React.useState(false);
  const handleCopy = async () => {
    try {
      await navigator.clipboard.writeText(content);
      setCopied(true);
      setTimeout(() => setCopied(false), 3000);
    } catch (err) {
      console.error('Failed to copy content:', err);
      alert('Failed to copy content. Please try selecting and copying manually.');
    }
  };
  return <div className="copy-all-container">
      <div className="copy-all-content">
        <div className="copy-all-title">Copy Integration Instructions</div>
        <div className="copy-all-description">
          Click to copy the entire page content to paste into your AI coding
          assistant
        </div>
      </div>
      <button className={`copy-all-button${copied ? ' copied' : ''}`} onClick={handleCopy} type="button">
        {copied ? 'Page Copied' : label}
      </button>
    </div>;
};

<Tip>
  **Using a Skills-compatible agent like Claude Code?** The [Greenflash agent skill](/features/agent-skill) handles this automatically. Run `/greenflash-onboard-agentic` instead of copying this prompt.
</Tip>

export const pageContent = `# 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:

| Type | Description |
|------|-------------|
| \\\`user_message\\\` | Input from the end user |
| \\\`assistant_message\\\` | Response from the assistant |
| \\\`system_message\\\` | System-level instructions |
| \\\`thought\\\` | Internal reasoning or planning |
| \\\`tool_call\\\` | Invocation of an external tool (requires \\\`tool_name\\\`) |
| \\\`observation\\\` | Result returned from a tool |
| \\\`final_response\\\` | The final user-facing response |
| \\\`retrieval\\\` | RAG or document retrieval |
| \\\`memory_read\\\` | Reading from agent memory |
| \\\`memory_write\\\` | Writing to agent memory |
| \\\`chain_start\\\` | Start of a multi-step chain |
| \\\`chain_end\\\` | End of a multi-step chain |
| \\\`embedding\\\` | Embedding operation |
| \\\`tool_error\\\` | Error from a tool |
| \\\`callback\\\` | Callback event |
| \\\`llm\\\` | LLM call |
| \\\`task\\\` | Task execution |
| \\\`workflow\\\` | Workflow 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:

| What | Message Type | Field |
|------|--------------|-------|
| User inputs | \\\`user_message\\\` | \\\`content\\\` |
| Assistant replies shown to users | \\\`assistant_message\\\` or \\\`final_response\\\` | \\\`content\\\` |
| Internal reasoning | \\\`thought\\\` | \\\`output\\\` |
| Tool invocations | \\\`tool_call\\\` | \\\`tool_name\\\` (required), \\\`input\\\` |
| Tool results | \\\`observation\\\` | \\\`output\\\` |
| Errors | \\\`tool_error\\\` | \\\`output\\\` |
| Multi-step flows | \\\`chain_start\\\`, \\\`chain_end\\\`, \\\`task\\\`, or \\\`workflow\\\` | as 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:**

\\\`\\\`\\\`python
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):**

\\\`\\\`\\\`python
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:**

\\\`\\\`\\\`python
# 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:**

\\\`\\\`\\\`python
# 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

\\\`\\\`\\\`python
# 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`;

<CopyAllButton content={pageContent} />

# 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](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:

| Type                | Description                                           |
| ------------------- | ----------------------------------------------------- |
| `user_message`      | Input from the end user                               |
| `assistant_message` | Response from the assistant                           |
| `system_message`    | System-level instructions                             |
| `thought`           | Internal reasoning or planning                        |
| `tool_call`         | Invocation of an external tool (requires `tool_name`) |
| `observation`       | Result returned from a tool                           |
| `final_response`    | The final user-facing response                        |
| `retrieval`         | RAG or document retrieval                             |
| `memory_read`       | Reading from agent memory                             |
| `memory_write`      | Writing to agent memory                               |
| `chain_start`       | Start of a multi-step chain                           |
| `chain_end`         | End of a multi-step chain                             |
| `embedding`         | Embedding operation                                   |
| `tool_error`        | Error from a tool                                     |
| `callback`          | Callback event                                        |
| `llm`               | LLM call                                              |
| `task`              | Task execution                                        |
| `workflow`          | Workflow 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:

| What                             | Message Type                                      | Field                           |
| -------------------------------- | ------------------------------------------------- | ------------------------------- |
| User inputs                      | `user_message`                                    | `content`                       |
| Assistant replies shown to users | `assistant_message` or `final_response`           | `content`                       |
| Internal reasoning               | `thought`                                         | `output`                        |
| Tool invocations                 | `tool_call`                                       | `tool_name` (required), `input` |
| Tool results                     | `observation`                                     | `output`                        |
| Errors                           | `tool_error`                                      | `output`                        |
| Multi-step flows                 | `chain_start`, `chain_end`, `task`, or `workflow` | as 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:**

```python theme={"theme":{"light":"github-light","dark":"vesper"}}
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):**

```python theme={"theme":{"light":"github-light","dark":"vesper"}}
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:**

```python theme={"theme":{"light":"github-light","dark":"vesper"}}
# 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:**

```python theme={"theme":{"light":"github-light","dark":"vesper"}}
# 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

```python theme={"theme":{"light":"github-light","dark":"vesper"}}
# 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](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
