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

# instance.send_message

> Send a conversation event over the active gRPC stream so Synap can anticipate context for the agent's next turn.

```python theme={null}
await sdk.instance.send_message(
    content,
    role="user",
    conversation_id=None,
    user_id=None,
    customer_id=None,
    session_id=None,
    event_type="user_message",
    metadata=None,
    tool_name=None,
    tool_args=None,
    search_queries=None,
    context_types=None,
)
```

<Note>
  Advanced: for real-time integrations. Requires an active [`instance.listen`](/sdk-reference/instance/listen) stream. If the stream is not active, this call raises `ListeningNotActiveError`.
</Note>

`send_message()` publishes a single agent activity event onto the bidirectional gRPC stream that `listen()` established. Each event tells the Synap platform what just happened in your agent (a user turn, an assistant reply, a tool call, or an explicit context request) so the platform can anticipate what context the agent will need next and push it back over the stream.

<Warning>
  **`send_message()` does not ingest the turn when you call it, but the turn is not discarded either.** Conversation events are persisted to conversation history, and when the conversation compacts (by default at 3,000 tokens or 10 messages), those raw turns are promoted into the same ingestion pipeline `memories.create()` uses.

  For an agent reporting conversation turns this is sufficient on its own; see [Agent Integration](/setup/agent-integration). It is deferred, not conditional: compaction fires at 3,000 tokens, 10 messages, **or 5 minutes of inactivity**, so every conversation gets there. Call [`memories.create`](/sdk-reference/memories/create) only for content that is not a conversation turn, or that must be retrievable sooner. Never for the same text you streamed, which would extract it twice.
</Warning>

### What the platform does with the event

| `event_type`        | Effect                                                                         |
| ------------------- | ------------------------------------------------------------------------------ |
| `user_message`      | Persisted to conversation history; signals a new turn                          |
| `assistant_message` | Persisted to conversation history; **triggers anticipation for the next turn** |
| `tool_call`         | Observed for situational awareness (informative, not a trigger)                |
| `context_request`   | `search_queries` / `context_types` used as direct anticipation hints           |

Persisted turns feed both layers of context. They advance the conversation toward automatic [context compaction](/sdk-reference/conversation-context/compact) once it crosses the configured token or message threshold, and that same compaction promotes the raw turns into long-term memory.

<Warning>
  A `user_message` or `assistant_message` is persisted only when **both** `user_id` and `customer_id` are present. If either is missing, the platform skips persistence and logs server-side, and your application receives no error and no exception. On B2C, pass your user identifier as both.
</Warning>

<Tip>
  Emit `assistant_message` **after** your agent produces its reply, not before. Anticipation runs between turns, so this event is what warms the cache for the *next* turn.
</Tip>

### Parameters

<ParamField path="content" type="string" required>
  The message content. For `user_message` and `assistant_message` events this is the natural-language turn; for `tool_call` events it can be a short description of the tool invocation.
</ParamField>

<ParamField path="role" type="string" default="user">
  Either `"user"` or `"assistant"`.
</ParamField>

<ParamField path="conversation_id" type="string">
  External identifier for the conversation this event belongs to. Required to associate the event with the right conversation scope. Must be a valid UUID [registered via `record_message`](/concepts/context-end-to-end#short-term-context).
</ParamField>

<ParamField path="user_id" type="string">
  External user identifier. Omit for customer- or client-scope events.
</ParamField>

<ParamField path="customer_id" type="string">
  External customer identifier. Required on B2B; auto-resolved on B2C. See [B2C vs B2B](/concepts/memory-scopes#b2c-vs-b2b-which-scopes-apply-to-you).
</ParamField>

<ParamField path="session_id" type="string">
  External session identifier.
</ParamField>

<ParamField path="event_type" type="string" default="user_message">
  The kind of event being reported. Common values: `user_message`, `assistant_message`, `tool_call`, `context_request`.
</ParamField>

<ParamField path="metadata" type="dict[str, str]">
  Additional string key-value metadata attached to the event.
</ParamField>

<ParamField path="tool_name" type="string">
  For `tool_call` events: the name of the tool the agent is invoking. The platform uses this to classify the tool call and anticipate the agent's next data needs.
</ParamField>

<ParamField path="tool_args" type="dict">
  For `tool_call` events: a JSON-encodable arguments dict for the tool invocation.
</ParamField>

<ParamField path="search_queries" type="list[string]">
  For `tool_call` or `context_request` events: the retrieval queries the agent plans to run. Used as direct anticipation hints.
</ParamField>

<ParamField path="context_types" type="list[string]">
  For `tool_call` or `context_request` events: the memory categories the agent plans to fetch.
</ParamField>

### Returns

Returns `None`. The coroutine resolves once the event has been written to the stream.

### Example

```python theme={null}
import uuid

# conversation_id must be a valid UUID; reuse the one for the active conversation
conversation_id = str(uuid.uuid4())

await sdk.instance.send_message(
    content="search_orders",
    role="assistant",
    event_type="tool_call",
    conversation_id=conversation_id,
    user_id="user_789",
    customer_id="cust_456",
    tool_name="search_orders",
    tool_args={"status": "shipped", "limit": 5},
    search_queries=["recent shipped orders"],
    context_types=["order_history"],
)
```

### Raises

* `ListeningNotActiveError`: when [`instance.listen`](/sdk-reference/instance/listen) has not been called or the stream has been closed.

See [Error Codes](/sdk-reference/errors) for the full SDK exception hierarchy.

### See also

* [Real-Time Anticipation](/concepts/real-time-anticipation): how streaming and ingestion fit together.
* [instance.listen](/sdk-reference/instance/listen): open the stream this method writes to.
* [instance.stop\_listening](/sdk-reference/instance/stop-listening): close the stream.
* [memories.create](/sdk-reference/memories/create): the call that actually creates memories.
