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

# Source-owned Memories

> Upserte und lösche Source-of-Record-Memories nach (endUserId, source, externalId, version) – idempotent, crash-safe und out-of-order-sicher zugestellt.

Nutze die Lifecycle-Oberfläche, wenn Memories Datensätze spiegeln, die deiner Datenbank gehören – CRM-Zeilen, Profilfelder, Tickets. Anders als das anfüge-nur `save` haben diese Memories eine durable Identität und Versionshistorie, sodass Korrekturen und Deletes aus der Source of Truth genau einmal, in Reihenfolge angewendet werden, egal wie chaotisch die Zustellung ist.

## Das Identitätsmodell

Eine source-owned Memory wird durch `(endUserId, source, externalId)` identifiziert. `version` muss immer dann steigen, wenn sich der Quelldatensatz ändert.

```ts theme={null}
await conare.memories.upsertSource({
  endUserId: "u_123",
  source: "crm-profile",
  externalId: "profile_123",
  version: 7,
  occurredAt: "2026-07-16T10:30:00Z",
  content: "User prefers smaller islands.",
  idempotencyKey: "profile_123:v7",
});
```

## Zustellgarantien

Mutationen sind synchron, und die Regeln sind einfach:

* **Dieselbe Version + Payload erneut senden** liefert dieselbe durable Quittung. Nach einem Crash erneut zu senden ist sicher – das *ist* die Crash-Recovery-Story.
* **Eine andere Payload für eine bestehende Version** liefert `409`. Versionen sind immutable.
* **Eine verzögerte ältere Version** liefert `409` und überschreibt niemals neueren Inhalt oder lässt eine gelöschte Memory wiederauferstehen.

Deletes folgen derselben Versionierung:

```ts theme={null}
await conare.memories.deleteSource({
  endUserId: "u_123",
  source: "crm-profile",
  externalId: "profile_123",
  version: 8,
  occurredAt: "2026-07-17T09:00:00Z",
  idempotencyKey: "profile_123:v8",
});
```

Der Delete ist durable: Retries konvergieren zur ursprünglichen Quittung, und verzögerte Upserts mit niedrigeren Versionen können die Memory nicht neu anlegen.

## Bootstrapping mit einer Outbox

Spiegele bestehende Datensätze in großen Mengen per `lifecycleBatch` – bis zu 100 Items und 1 MiB aggregierter Content pro Call. Es gibt keinen serverseitigen Import-Job; eine clientseitige Outbox, die nach einem Crash erneut sendet, ist die vollständige Resume-Story:

```ts theme={null}
const result = await conare.memories.lifecycleBatch({
  endUserId: "u_123",
  items: [{
    source: "crm-profile",
    externalId: "profile_123",
    version: 7,
    occurredAt: "2026-07-16T10:30:00Z",
    content: "User prefers smaller islands.",
  }],
});

for (const [index, item] of result.items.entries()) {
  if (item.success || item.code === "stale_source_version") {
    outbox.markDone(index); // diese Version (oder neuer) ist durable
  } else {
    outbox.scheduleRetry(index, item.code);
  }
}
```

<Note>
  `result.success` bedeutet nur „der Batch wurde verarbeitet" – prüfe stets die Ergebnisse pro Item. Markiere eine Zeile bei `success` **oder** `stale_source_version` als erledigt (beides heißt, dass die Version bereits durable ist), und sende dann den nächsten Batch.
</Note>

## Zurücklesen

`GET /api/v1/memories/{externalId}` liefert die Memory und ihre zuletzt angewandte Quittung – nützlich, um Konvergenz nach einer Migration zu verifizieren.
