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 messageType to describe what the agent is doing. The available messageType 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 toolName)
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_calltoolName (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:
  • externalMessageId on each message
  • parentExternalMessageId 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 externalMessageId are automatically deduplicated within a conversation. If you resend a batch of messages that includes previously ingested messages (identified by their externalMessageId), 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.
Fire-and-forget (recommended):
client.messages.create({
  productId: "YOUR_PRODUCT_ID",
  externalUserId: "USER_ID",
  externalConversationId: "CONVO_ID",
  messages: [
    // User query
    {
      role: "user",
      content: "I got charged twice for my subscription this month.",
    },
    // Agent reasoning (internal, not user-facing)
    {
      externalMessageId: "thought-account-lookup",
      messageType: "thought",
      output: "Customer reports double charge. Need to verify identity and pull account details to understand the situation.",
    },
    // Tool invocation
    {
      externalMessageId: "tool-account-details",
      messageType: "tool_call",
      toolName: "account_details",
      input: {
        searchQuery: "Michael Chen",
        includeBillingHistory: true,
      },
    },
    // Tool result
    {
      externalMessageId: "tool-account-details-result",
      messageType: "observation",
      parentExternalMessageId: "tool-account-details",
      output: {
        recentCharges: [
          { 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.",
    },
  ],
}).catch(err => console.error('Greenflash error:', err));
With await (if you need to wait):
await client.messages.create({
  productId: "YOUR_PRODUCT_ID",
  externalUserId: "USER_ID",
  externalConversationId: "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. Fire-and-forget example:
// 1. User message arrives
client.messages.create({
  productId: "YOUR_PRODUCT_ID",
  externalUserId: "USER_ID",
  externalConversationId: "CONVO_ID",
  messages: [{
    role: "user",
    content: "I got charged twice for my subscription this month.",
  }],
}).catch(err => console.error('Greenflash error:', err));

// 2. Agent begins reasoning (internal)
client.messages.create({
  productId: "YOUR_PRODUCT_ID",
  externalUserId: "USER_ID",
  externalConversationId: "CONVO_ID",
  messages: [{
    externalMessageId: "thought-account-lookup",
    messageType: "thought",
    output: "Customer reports double charge. Need to pull account details.",
  }],
}).catch(err => console.error('Greenflash error:', err));

// 3. Agent invokes tool
client.messages.create({
  productId: "YOUR_PRODUCT_ID",
  externalUserId: "USER_ID",
  externalConversationId: "CONVO_ID",
  messages: [{
    externalMessageId: "tool-account-details",
    messageType: "tool_call",
    toolName: "account_details",
    input: { searchQuery: "Michael Chen", includeBillingHistory: true },
  }],
}).catch(err => console.error('Greenflash error:', err));

// 4. Tool returns result
client.messages.create({
  productId: "YOUR_PRODUCT_ID",
  externalUserId: "USER_ID",
  externalConversationId: "CONVO_ID",
  messages: [{
    externalMessageId: "tool-account-details-result",
    messageType: "observation",
    parentExternalMessageId: "tool-account-details",
    output: { duplicateChargeConfirmed: true, refundEligible: true },
  }],
}).catch(err => console.error('Greenflash error:', err));

// 5. Agent responds to user (user-facing)
client.messages.create({
  productId: "YOUR_PRODUCT_ID",
  externalUserId: "USER_ID",
  externalConversationId: "CONVO_ID",
  messages: [{
    role: "assistant",
    content: "I found the duplicate charge. Processing your refund now.",
  }],
}).catch(err => console.error('Greenflash error:', err));
Both approaches are correct. Your job is to adapt to the existing codebase, not force a pattern.

Fire-and-Forget Logging

For logging, you typically don’t want to block your agent execution while waiting for the Greenflash API call to complete. Use fire-and-forget by not awaiting the promise:
// Fire-and-forget: don't await, just add .catch() to handle errors
client.messages.create({
  productId: productId,
  externalUserId: userId,
  externalConversationId: convoId,
  messages: messageList,
}).catch(err => console.error('Greenflash logging error:', err));
Note: The .catch() is recommended to prevent unhandled promise rejections from crashing your app, but the logging call runs in the background without blocking your agent.

TypeScript SDK Requirements

We are already using the Greenflash TypeScript SDK. You must:
  • Use the TypeScript SDK syntax
  • Call client.messages.create(...)
  • Do not make raw HTTP POST requests
// Correct: Use the SDK
client.messages.create({
  productId: productId,
  externalUserId: userId,
  externalConversationId: convoId,
  messages: [{
    messageType: "tool_call",
    input: {...},
    externalMessageId: "tool-call-1"
  }],
}).catch(err => console.error('Greenflash error:', err));
Reference: Follow the SDK conventions shown in the official docs: https://docs.greenflash.ai/sdks/typescript

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 messageType
  • 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. TypeScript SDK-based message submission
  5. A submission strategy (batch vs incremental) that fits the existing code