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

# Message

> Normalized inbound message surface for LangGraph.

This page describes the `Message` type from `aion-authoring-langgraph`.

The goal is to give LangGraph authors a stable, provider-neutral view of the
current inbound message without making them inspect raw Slack, Telegram, or
other provider payloads directly.

## Properties

| Property  | Purpose                                                                                  |
| --------- | ---------------------------------------------------------------------------------------- |
| `id`      | Stable message identifier for the inbound turn                                           |
| `text`    | Plain-text body when one exists                                                          |
| `user.id` | Identifier for the user who triggered the event                                          |
| `thread`  | Bound `Thread` object for the current conversation                                       |
| `context` | [`AionRuntimeContext`](/sdk/python/messaging/runtime-context) for the current invocation |

## Methods

| Method                                               | Purpose                                                   |
| ---------------------------------------------------- | --------------------------------------------------------- |
| `reply(content, *, metadata=None)`                   | Convenience wrapper around `message.thread.reply(...)`    |
| `react(key, *, operation="add", display_value=None)` | Express a normalized reaction against the current message |

## Methods Detail

### `reply(content, *, metadata=None)`

Add a durable reply to the message in the current thread.

This is a convenience wrapper around `message.thread.reply(...)`. It sends the reply to the same thread where the message arrived.

| Parameter  | Type                                   | Description                                                         |
| ---------- | -------------------------------------- | ------------------------------------------------------------------- |
| `content`  | `str` \| `AIMessage` \| async iterator | Message content: plain text, LangChain message, or stream of chunks |
| `metadata` | `Optional[dict]`                       | Optional metadata to attach to the reply                            |

**Returns:** The message object that was sent, or `None` if the reply failed.

**Example:**

```python theme={null}
async def node(state: State, runtime: Runtime[AionRuntimeContext]) -> dict:
    message = thread.message
    
    if message:
        # Simple text reply
        await message.reply("Got it!")
        
        # Reply with metadata
        await message.reply(
            "Processing complete",
            metadata={"status": "done", "duration": 5.2}
        )
```

### `react(key, *, operation="add", display_value=None)`

Express a normalized reaction against the current message.

| Parameter       | Type                       | Description                                                          |
| --------------- | -------------------------- | -------------------------------------------------------------------- |
| `key`           | `str`                      | Reaction identifier (e.g., `thumbsup`, `heart`, `thinking_face`)     |
| `operation`     | `Literal["add", "remove"]` | Whether to add or remove the reaction (default: `"add"`)             |
| `display_value` | `Optional[str]`            | Human-readable reaction text; if not provided, uses the reaction key |

Requires an inbound event with `context_id` and `message_id` in its payload. Logs a warning if the event context is unavailable.

## Example

```python theme={null}
async def node(state: State, runtime: Runtime[AionRuntimeContext]) -> dict:
    thread = Thread.from_context(runtime.context)
    message = thread.message

    if message and message.text.lower().startswith("thanks"):
        # Add a thumbsup reaction
        await message.react("thumbsup")
        await message.reply("You're welcome.")
        return {}

    if message and message.text.lower().startswith("ugh"):
        # Add a custom display reaction and then remove it
        await message.react("thinking_face", display_value="Let me think...")
        # Later, remove the reaction
        await message.react("thinking_face", operation="remove")
        return {}

    return {}
```

## Relation to Other Event Kinds

Not every inbound turn is a plain message. Reaction, command, and card-action
turns are better modeled as event kinds with specialized metadata.

For those turns:

* `runtime.context.event` should remain authoritative
* `thread.message` may still be populated when the provider exposes a
  meaningful message anchor
* handler registration should happen through
  [Event Handlers](/sdk/langgraph/api/event-handlers)

## Related Pages

* [AionRuntimeContext](/sdk/langgraph/api/runtime-context)
* [Thread](/sdk/langgraph/api/thread)
* [Event Handlers](/sdk/langgraph/api/event-handlers)
