> ## 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.

# Mémoires détenues par la source

> Upsertez et supprimez des mémoires source de vérité par (endUserId, source, externalId, version), avec livraison idempotente, sûre au crash et hors-ordre.

Utilisez la surface de cycle de vie lorsque les mémoires reflètent des enregistrements détenus par votre base de données — lignes CRM, champs de profil, tickets. Contrairement au `save` en ajout seul, ces mémoires ont une identité durable et un historique de versions, si bien que les corrections et suppressions issues de la source de vérité s'appliquent exactement une fois, dans l'ordre, quelle que soit la mise en pagaille de la livraison.

## Le modèle d'identité

Une mémoire détenue par la source est identifiée par `(endUserId, source, externalId)`. `version` doit augmenter chaque fois que l'enregistrement source change.

```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",
});
```

## Garanties de livraison

Les mutations sont synchrones, et les règles sont simples :

* **Réessayer la même version + charge** renvoie le même reçu durable. Renvoyer après un crash est sûr — c'est *ça* l'histoire de récupération après crash.
* **Une charge différente pour une version existante** renvoie `409`. Les versions sont immuables.
* **Une version plus ancienne retardée** renvoie `409` et n'écrase jamais un contenu plus récent ni ne ressuscite une mémoire supprimée.

Les suppressions suivent le même versionnage :

```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",
});
```

La suppression est durable : les tentatives convergent vers le reçu d'origine, et les upserts retardés avec des versions plus faibles ne peuvent recréer la mémoire.

## Amorçage avec un outbox

Reflétez en masse les enregistrements existants via `lifecycleBatch` — jusqu'à 100 éléments et 1 MiB de contenu agrégé par appel. Il n'y a pas de job d'import côté serveur ; un outbox côté client qui renvoie après un crash est toute l'histoire de reprise :

```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); // that version (or newer) is durable
  } else {
    outbox.scheduleRetry(index, item.code);
  }
}
```

<Note>
  `result.success` signifie seulement « le batch a été traité » — vérifiez toujours les résultats par élément. Marquez une ligne comme terminée sur `success` **ou** `stale_source_version` (les deux signifient que la version est déjà durable), puis envoyez le batch suivant.
</Note>

## Relecture

`GET /api/v1/memories/{externalId}` renvoie la mémoire et son dernier reçu appliqué — utile pour vérifier la convergence après une migration.
