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

# Memory Operations

> Store, retrieve, and manage memories in Nebula

<Info>New to Nebula? Start with [Core Concepts](/guides/concepts) to understand the architecture.</Info>

## Storing Memories

<CodeGroup>
  ```python Python theme={null}
  from nebula import Nebula
  nebula = Nebula()
  collection = nebula.collections.retrieve_by_name("research").results

  # Single memory
  created = nebula.memories.create(
      collection_id=collection.id,
      raw_text="Machine learning automates model building",
      metadata={"topic": "AI"},
  ).results
  memory_id = created.id

  # Multiple memories — the SDK has no batch endpoint, so loop on the wire
  memory_ids = []
  for item in [
      {"raw_text": "Neural networks...", "metadata": {"type": "concept"}},
      {"raw_text": "Deep learning...", "metadata": {"type": "concept"}},
  ]:
      res = nebula.memories.create(collection_id=collection.id, **item).results
      memory_ids.append(res.id)
  ```

  ```javascript JavaScript theme={null}
  import Nebula from '@nebula-ai/sdk';
  const client = new Nebula({ apiKey: process.env.NEBULA_API_KEY });
  const { results: collection } = await client.collections.retrieveByName('research');

  // Single memory
  const { results: created } = await client.memories.create({
    collection_id: collection.id,
    raw_text: 'Machine learning automates model building',
    metadata: { topic: 'AI' },
  });
  const memoryId = created.id;

  // Multiple memories — the SDK has no batch endpoint; fan out with Promise.all
  const items = [
    { raw_text: 'Neural networks...', metadata: { type: 'concept' } },
    { raw_text: 'Deep learning...', metadata: { type: 'concept' } },
  ];
  const memoryIds = (
    await Promise.all(
      items.map(item =>
        client.memories.create({ collection_id: collection.id, ...item })
      )
    )
  ).map(r => r.results.id);
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.zeroset.com/v1/memories" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -d '{
      "collection_id": "COLLECTION_ID",
      "kind": "document",
      "raw_text": "Machine learning automates model building",
      "metadata": {"topic": "AI"}
    }'
  ```
</CodeGroup>

<Warning>The use of `metadata` is highly discouraged. Nebula already consolidates all the semantics from your content automatically.</Warning>

<Info>Memory creation is asynchronous. Content becomes searchable after Nebula finishes extraction (typically a few seconds). The API returns a `202` status with the memory ID immediately.</Info>

### Conversation Messages

<CodeGroup>
  ```python Python theme={null}
  support = nebula.collections.retrieve_by_name("support").results

  # Create conversation
  conv = nebula.memories.create(
      collection_id=support.id,
      kind="conversation",
      messages=[{"content": "Hello! How can I help?", "role": "assistant"}],
  ).results
  conv_id = conv.id

  # Add to same conversation
  nebula.memories.append(
      conv_id,
      collection_id=support.id,
      messages=[{"content": "I need help with my account", "role": "user"}],
  )
  ```

  ```javascript JavaScript theme={null}
  const { results: support } = await client.collections.retrieveByName('support');

  const { results: conv } = await client.memories.create({
    collection_id: support.id,
    kind: 'conversation',
    messages: [{ content: 'Hello! How can I help?', role: 'assistant' }],
  });
  const convId = conv.id;

  await client.memories.append(convId, {
    collection_id: support.id,
    messages: [{ content: 'I need help with my account', role: 'user' }],
  });
  ```

  ```bash cURL theme={null}
  curl -X POST "https://api.zeroset.com/v1/memories" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -d '{
      "collection_id": "COLLECTION_ID",
      "kind": "conversation",
      "messages": [
        {"role": "assistant", "content": "Hello! How can I help?"},
        {"role": "user", "content": "I need help with my account"}
      ]
    }'
  ```
</CodeGroup>

See [Conversations Guide](/guides/conversations) for multi-turn patterns.

### Document Upload

Upload a document as raw text, pre-chunked text, or a file. File processing (OCR/transcription/text extraction) happens automatically.

<CodeGroup>
  ```python Python theme={null}
  from nebula import Nebula

  nebula = Nebula()
  collection = nebula.collections.retrieve_by_name("my-collection").results

  # Upload text
  doc = nebula.memories.create(
      collection_id=collection.id,
      raw_text="Machine learning is a subset of AI...",
      kind="document",
      metadata={"title": "ML Intro"},
  ).results
  doc_id = doc.id

  # Upload pre-chunked content
  doc = nebula.memories.create(
      collection_id=collection.id,
      chunks=["Chapter 1...", "Chapter 2..."],
      kind="document",
      metadata={"title": "My Doc"},
  ).results
  doc_id = doc.id
  ```

  ```javascript JavaScript theme={null}
  import Nebula from '@nebula-ai/sdk';

  const client = new Nebula({ apiKey: process.env.NEBULA_API_KEY });
  const { results: collection } = await client.collections.retrieveByName('my-collection');

  // Upload text
  const { results: doc } = await client.memories.create({
    collection_id: collection.id,
    raw_text: 'Machine learning is a subset of AI...',
    kind: 'document',
    metadata: { title: 'ML Intro' },
  });
  const docId = doc.id;

  // Upload pre-chunked content
  const { results: doc2 } = await client.memories.create({
    collection_id: collection.id,
    chunks: ['Chapter 1...', 'Chapter 2...'],
    kind: 'document',
    metadata: { title: 'My Doc' },
  });
  const docId2 = doc2.id;
  ```

  ```bash cURL theme={null}
  # Upload file (base64 encoded)
  curl -X POST "https://api.zeroset.com/v1/memories" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -d '{
      "collection_id": "COLLECTION_ID",
      "content_parts": [{
        "type": "document",
        "data": "BASE64_ENCODED_FILE_DATA",
        "media_type": "application/pdf",
        "filename": "document.pdf"
      }],
      "metadata": {"title": "Research Paper"}
    }'
  ```
</CodeGroup>

<Tip>Inline base64 uploads are limited to \~5MB per file part; larger files use a presigned upload flow (max 100MB).</Tip>

### Multiple Documents

The Python SDK exposes a single `memories.create()` per memory; loop to store multiple documents. To group conversation turns, create one memory with `kind="conversation"` and a `messages` list.

<CodeGroup>
  ```python Python theme={null}
  from nebula import Nebula
  nebula = Nebula()

  # Store multiple documents
  ids = []
  for text in ["First document", "Second document", "Third document"]:
      res = nebula.memories.create(collection_id=collection.id, raw_text=text).results
      ids.append(res.id)

  # Store an entire conversation in one memory
  conv = nebula.memories.create(
      collection_id=collection.id,
      kind="conversation",
      messages=[
          {"content": "Hello!", "role": "user"},
          {"content": "Hi there!", "role": "assistant"},
      ],
  ).results
  ```

  ```javascript JavaScript theme={null}
  // Store multiple documents
  const texts = ['First document', 'Second document', 'Third document'];
  const ids = (
    await Promise.all(
      texts.map(raw_text =>
        client.memories.create({ collection_id: collection.id, raw_text })
      )
    )
  ).map(r => r.results.id);

  // Store an entire conversation in one memory
  const { results: conv } = await client.memories.create({
    collection_id: collection.id,
    kind: 'conversation',
    messages: [
      { content: 'Hello!', role: 'user' },
      { content: 'Hi there!', role: 'assistant' },
    ],
  });
  ```
</CodeGroup>

## Retrieving Memories

<CodeGroup>
  ```python Python theme={null}
  # Get by ID
  memory = nebula.memories.retrieve(memory_id).results

  # List memories in collection
  memories = nebula.memories.list(collection_ids=[collection.id], limit=50).results
  ```

  ```javascript JavaScript theme={null}
  // Get by ID
  const { results: memory } = await client.memories.retrieve(memoryId);

  // List memories in collection
  const { results: memories } = await client.memories.list({
    collection_ids: [collection.id],
    limit: 50,
  });
  ```

  ```bash cURL theme={null}
  # Get by ID
  curl -X GET "https://api.zeroset.com/v1/memories/YOUR_MEMORY_ID" \
    -H "Authorization: Bearer YOUR_API_KEY"

  # List memories
  curl -X GET "https://api.zeroset.com/v1/memories?limit=50" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```
</CodeGroup>

<Info>For semantic search (finding by meaning), see the [Search Guide](/guides/search).</Info>

## Deleting Memories

<CodeGroup>
  ```python Python theme={null}
  # Delete single
  nebula.memories.delete(memory_id)

  # Delete multiple
  nebula.memories.delete_many(body=[memory_id_1, memory_id_2, memory_id_3])
  ```

  ```javascript JavaScript theme={null}
  await client.memories.delete(memoryId);
  await client.memories.deleteMany({ body: [memoryId1, memoryId2, memoryId3] });
  ```

  ```bash cURL theme={null}
  curl -X DELETE "https://api.zeroset.com/v1/memories/YOUR_MEMORY_ID" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```
</CodeGroup>

<Warning>Deletion is permanent and cannot be undone.</Warning>

## Bulk Operations

Use `memories.list()` with `metadata_filters` to target a set, then delete or update metadata in batches. The endpoint caps `limit` at 1000 per request, so larger result sets need pagination.

<Tip>For high-fan-out client work, throttle with chunked `Promise.all` (e.g., `p-limit`) instead of one unbounded call. The TS SDK already retries 429s with backoff, but bounded concurrency keeps socket pressure and tail latency reasonable.</Tip>

<CodeGroup>
  ```python Python theme={null}
  import json

  docs = nebula.collections.retrieve_by_name("docs").results
  filters = json.dumps({"metadata.status": {"$eq": "archived"}})
  PAGE = 1000  # endpoint maximum

  # Bulk delete: re-list with offset=0 each iteration since the previous
  # page is gone after delete_many.
  while True:
      page = nebula.memories.list(
          collection_ids=[docs.id],
          metadata_filters=filters,
          limit=PAGE,
      )
      if not page.results:
          break
      nebula.memories.delete_many(body=[m.id for m in page.results])
      if len(page.results) < PAGE:
          break

  # Bulk metadata update: rows aren't removed, so walk by offset until
  # total_entries is covered.
  offset = 0
  while True:
      page = nebula.memories.list(
          collection_ids=[docs.id],
          metadata_filters=filters,
          limit=PAGE,
          offset=offset,
      )
      for m in page.results:
          nebula.memories.update(
              m.id,
              metadata={"archived": True},
              merge_metadata=True,
          )
      offset += len(page.results)
      if not page.results or offset >= page.total_entries:
          break
  ```

  ```javascript JavaScript theme={null}
  const { results: docs } = await client.collections.retrieveByName('docs');
  const filters = JSON.stringify({ 'metadata.status': { $eq: 'archived' } });
  const PAGE = 1000; // endpoint maximum

  // Bulk delete: re-list with offset=0 each iteration.
  while (true) {
    const page = await client.memories.list({
      collection_ids: [docs.id],
      metadata_filters: filters,
      limit: PAGE,
    });
    if (page.results.length === 0) break;
    await client.memories.deleteMany({ body: page.results.map(m => m.id) });
    if (page.results.length < PAGE) break;
  }

  // Bulk metadata update: walk by offset until total_entries is covered.
  let offset = 0;
  while (true) {
    const page = await client.memories.list({
      collection_ids: [docs.id],
      metadata_filters: filters,
      limit: PAGE,
      offset,
    });
    await Promise.all(
      page.results.map(m =>
        client.memories.update(m.id, {
          metadata: { archived: true },
          merge_metadata: true,
        })
      )
    );
    offset += page.results.length;
    if (page.results.length === 0 || offset >= page.total_entries) break;
  }
  ```

  ```bash cURL theme={null}
  curl -G "https://api.zeroset.com/v1/memories" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    --data-urlencode 'collection_ids=COLLECTION_ID' \
    --data-urlencode 'limit=1000' \
    --data-urlencode 'offset=0' \
    --data-urlencode 'metadata_filters={"metadata.status":{"$eq":"archived"}}'

  curl -X POST "https://api.zeroset.com/v1/memories/delete" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '["MEMORY_ID_1", "MEMORY_ID_2"]'
  ```
</CodeGroup>

<Info>Use `memories.append()` to append content. `memories.update()` only updates name, metadata, or collection associations.</Info>

## Source Operations

Memories contain sources (messages in conversations, sections in documents). The retrieve response exposes them on `chunks` as read-only provenance — opaque records you can inspect but not edit in place. To change stored content, update the memory metadata or append replacement content as a new memory entry.

<CodeGroup>
  ```python Python theme={null}
  memory = nebula.memories.retrieve(memory_id).results
  # `chunks` is typed `List[object]`; on retrieve each chunk is the chunk text string.
  for chunk in memory.chunks or []:
      print(chunk)
  ```

  ```javascript JavaScript theme={null}
  const { results: memory } = await client.memories.retrieve(memoryId);
  // `chunks` is typed `unknown[]`; on retrieve each chunk is the chunk text string.
  memory.chunks?.forEach(chunk => console.log(chunk));
  ```

  ```bash cURL theme={null}
  # Get memory with chunks
  curl -X GET "https://api.zeroset.com/v1/memories/YOUR_MEMORY_ID" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```
</CodeGroup>

## Next Steps

* [Search](/guides/search) - Semantic search and filtering
* [Collections](/guides/collections) - Organize memories into collections
