# Store memories

Engram supports three [content types](../engram-concepts/input-data-types.md) for storing memories. Each content type is a different entrypoint into the same [pipeline](../engram-concepts/pipelines.md).

::::accordion{title="All examples below use a connected client"}
See [Connect to Engram](../engram/quickstart.md#step-3-connect-to-engram) for how to instantiate one.

:::code-group{sync="languages"}
```python title="Python"
import os
from engram import EngramClient

client = EngramClient(api_key=os.environ["ENGRAM_API_KEY"])
```

```python title="Python (Async)"
import os
from engram import AsyncEngramClient

client = AsyncEngramClient(api_key=os.environ["ENGRAM_API_KEY"])
```

```bash title="cURL"
export ENGRAM_API_KEY="eng_..."
```
:::
::::

## String content

Send raw text and let Engram extract structured memories from it.

:::code-group{sync="languages"}
```python title="Python"
run = client.memories.add(
    "The user prefers dark mode and works primarily in Python. They are building a RAG application.",
    user_id=test_user_id,
    group="default",
)

print(run.run_id)
print(run.status)
```

```pyindent title="Python (Async)"
run = await client.memories.add(
    "The user prefers dark mode and works primarily in Python. They are building a RAG application.",
    user_id=test_user_id,
)

print(run.run_id)
print(run.status)
```

```bash title="cURL"
curl -X POST https://api.engram.weaviate.io/v1/memories \
  -H "Authorization: Bearer $ENGRAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "string": {
        "content": [
          "The user prefers dark mode and works primarily in Python. They are building a RAG application."
        ]
      }
    },
    "user_id": "user-uuid",
    "group": "default"
  }'
```
:::

The pipeline extracts individual facts from the text (e.g. "prefers dark mode", "works in Python") and stores them as separate memories.

## Conversation content

Send multi-turn messages and let Engram extract memories from the dialogue. You can send new messages as they happen — there is no need to wait until a conversation is finished.

:::code-group{sync="languages"}
```python title="Python"
run = client.memories.add(
    [
        {"role": "user", "content": "I just moved to Berlin and I am looking for a good coffee shop."},
        {"role": "assistant", "content": "Welcome to Berlin! Here are some popular coffee shops in the city..."},
        {"role": "user", "content": "I prefer specialty coffee, not chains."},
    ],
    user_id=test_user_id,
    group="default",
)

print(run.run_id)
print(run.status)
```

```pyindent title="Python (Async)"
run = await client.memories.add(
    [
        {"role": "user", "content": "I just moved to Berlin and I am looking for a good coffee shop."},
        {"role": "assistant", "content": "Welcome to Berlin! Here are some popular coffee shops in the city..."},
        {"role": "user", "content": "I prefer specialty coffee, not chains."},
    ],
    user_id=test_user_id,
)
```

```bash title="cURL"
curl -X POST https://api.engram.weaviate.io/v1/memories \
  -H "Authorization: Bearer $ENGRAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "conversation": {
        "messages": [
          {
            "role": "user",
            "content": "I just moved to Berlin and I am looking for a good coffee shop."
          },
          {
            "role": "assistant",
            "content": "Welcome to Berlin! Here are some popular coffee shops in the city..."
          },
          {
            "role": "user",
            "content": "I prefer specialty coffee, not chains."
          }
        ]
      }
    },
    "user_id": "user-uuid",
    "group": "default"
  }'
```
:::

The pipeline reads the messages and extracts relevant facts (e.g. "lives in Berlin", "prefers specialty coffee").

## Pre-extracted content

If you've already extracted structured content, send it directly. This bypasses the LLM extraction step, but the content still passes through the transform and commit pipeline stages.

:::code-group{sync="languages"}
```python title="Python"
run = client.memories.add(
    PreExtractedInput(items=[
        PreExtractedItem(content="User prefers dark mode", topic="UserKnowledge"),
        PreExtractedItem(content="User works in Python", topic="UserKnowledge"),
    ]),
    user_id=test_user_id,
    group="default",
)

print(run.run_id)
print(run.status)
```

```pyindent title="Python (Async)"
run = await client.memories.add(
    PreExtractedInput(items=[
        PreExtractedItem(content="User prefers dark mode", topic="UserKnowledge"),
        PreExtractedItem(content="User works in Python", topic="UserKnowledge"),
    ]),
    user_id=test_user_id,
)
```

```bash title="cURL"
curl -X POST https://api.engram.weaviate.io/v1/memories \
  -H "Authorization: Bearer $ENGRAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "pre_extracted": {
        "items": [
          { "content": "User prefers dark mode", "topic": "UserKnowledge" },
          { "content": "User works in Python", "topic": "UserKnowledge" }
        ]
      }
    },
    "user_id": "user-uuid",
    "group": "default"
  }'
```
:::

## Response

All three content types return the same response format:

```json
{
  "run_id": "run-uuid",
  "status": "running"
}
```

A successful response means the pipeline has started, not that the memories have been committed. In most cases you don't need to do anything else, since memories become available once the pipeline finishes. If you have a specific reason to confirm completion, you can use the `run_id` to [check the pipeline status](check-run-status.md).

## Optional parameters

| Parameter    | Type                    | Description                                                                                                                                            |
| ------------ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `user_id`    | string                  | [Scope](../engram-concepts/scopes.md) the memory to a specific user. Required if the target topic is user-scoped.                                      |
| `properties` | object\<string, string> | Custom [scope properties](../engram-concepts/scopes.md) (e.g. `{"conversation_id": "abc-123"}`). Must include every key any target topic is scoped by. |
| `group`      | string                  | Memory [group](../engram-concepts/groups.md) name (defaults to `default`)                                                                              |
| `root`       | string                  | Pipeline root name (for advanced pipeline configurations)                                                                                              |

:::callout{intent="info"}
Which parameters are required depends on the topic's [scoping](../engram-concepts/scopes.md) configuration, not the content type. If a topic is user-scoped, you must include `user_id`. If a topic is scoped by a custom property (e.g. `conversation_id`), pass it in `properties`. These scoping parameters apply equally to all three content types.
:::

## Questions and feedback

Have a question or feedback? Here's how to reach us.

::::card-grid
:::card{title="Community Forum" href="https://forum.weaviate.io/c/support" icon="messages-square"}
Ask questions and connect with other developers on our **Community forum**.
:::

:::card{title="Support" href="/guides/support-overview" icon="life-buoy"}
Weaviate Cloud user or customer? Find the right channel on the **Support page**.
:::
::::

## Related pages

- [Agents](./agents-index.md)
- [AI-assisted Weaviate code generation](./ai-assisted-vibe-coding-index.md)
- [APIs](./apis-index.md)
- [Authorization and authentication](./authorization-and-authentication-index.md)
- [Benchmarks](./benchmarks-index.md)
- [Best practices](./best-practices-index.md)
- [Client libraries](./clients-index.md)
- [Client Libraries / SDKs](./client-libraries-index.md)
- [Cloud](./cloud-index.md)
- [Cloud account management](./cloud-account-management-index.md)

# Agent Instructions

This portal answers questions programmatically. To receive a synthesized,
source-cited answer instead of crawling page by page, append the `?ask=`
query parameter to any page URL on this site:

    /guides/quickstart?ask=how+do+I+authenticate

Optional parameters:

- `&goal=<what-you-are-trying-to-do>` steers the answer toward your
  objective (e.g. `&goal=write+a+python+client`).
- `&version=<label>` scopes the answer to a mounted version when the
  portal publishes more than one.

The response is `text/markdown`: the answer followed by a `# Sources` list
of the portal pages it was grounded in. Status codes are the contract:

- `200` — the answer; `402` — the portal owner’s plan or answer credits are
  exhausted (surface this to your operator; do NOT retry); `429` — you are
  rate-limited; back off for the `Retry-After` seconds; `503` — the answer
  lane is temporarily unavailable; fall back to crawling the `.md` pages.

For the full corpus map read `llms.txt` at the site root; for the tool
surface (search + page fetch as MCP tools) see `/mcp`.
