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

# Observations API

> Create, update, and retrieve memory observations

Observations are individual memories stored during a session. They can represent decisions, bugs, patterns, or any notable information.

## Add Observation

`POST /observations`

Create a new observation or update an existing one via deduplication.

### Request Body

<ParamField body="session_id" type="string" required>
  Session ID this observation belongs to
</ParamField>

<ParamField body="type" type="string" required>
  Observation type: `manual`, `architecture`, `bug`, `decision`, `pattern`, `config`, `discovery`, `learning`, `passive`
</ParamField>

<ParamField body="title" type="string" required>
  Short title for the observation (max 200 chars)
</ParamField>

<ParamField body="content" type="string" required>
  Full observation content (max 2000 chars, truncated automatically)
</ParamField>

<ParamField body="tool_name" type="string">
  Tool that generated this observation (e.g., "bash", "edit", "grep")
</ParamField>

<ParamField body="project" type="string">
  Project name for filtering
</ParamField>

<ParamField body="scope" type="string" default="project">
  Scope: `project` or `personal`
</ParamField>

<ParamField body="topic_key" type="string">
  Topic key for grouping related observations (e.g., "architecture/auth-flow")
</ParamField>

### Response

<ResponseField name="id" type="integer">
  The observation ID (auto-incremented)
</ResponseField>

<ResponseField name="status" type="string">
  Always "saved"
</ResponseField>

### Example

```bash theme={null}
curl -X POST http://127.0.0.1:7437/observations \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "session-abc123",
    "type": "bug",
    "title": "Auth token expiration issue",
    "content": "Users were experiencing unexpected logouts due to token expiration not being handled properly. Fixed by implementing refresh token rotation.",
    "project": "engram",
    "scope": "project"
  }'
```

```json theme={null}
{
  "id": 42,
  "status": "saved"
}
```

## Passive Capture

`POST /observations/passive`

Automatically extract and save learnings from structured text (e.g., session summaries).

### Request Body

<ParamField body="session_id" type="string" required>
  Session ID
</ParamField>

<ParamField body="content" type="string" required>
  Text containing "## Key Learnings:" or "## Aprendizajes:" section
</ParamField>

<ParamField body="project" type="string">
  Project name
</ParamField>

<ParamField body="source" type="string">
  Source identifier (e.g., "subagent-stop", "session-end")
</ParamField>

### Response

<ResponseField name="extracted" type="integer">
  Total learnings found in the text
</ResponseField>

<ResponseField name="saved" type="integer">
  New observations created
</ResponseField>

<ResponseField name="duplicates" type="integer">
  Learnings skipped (already exist)
</ResponseField>

### Example

```bash theme={null}
curl -X POST http://127.0.0.1:7437/observations/passive \
  -H "Content-Type: application/json" \
  -d '{
    "session_id": "session-abc123",
    "project": "engram",
    "content": "## Key Learnings:\n\n1. SQLite FTS5 requires careful query sanitization\n2. Deduplication works best with content hashing\n3. Topic keys enable cross-session continuity"
  }'
```

```json theme={null}
{
  "extracted": 3,
  "saved": 3,
  "duplicates": 0
}
```

## Get Recent Observations

`GET /observations/recent`

Retrieve recent observations with optional filtering.

### Query Parameters

<ParamField query="project" type="string">
  Filter by project name
</ParamField>

<ParamField query="scope" type="string">
  Filter by scope: `project` or `personal`
</ParamField>

<ParamField query="limit" type="integer" default="20">
  Maximum observations to return (1-100)
</ParamField>

### Response

Returns an array of observations (see schema below).

### Example

```bash theme={null}
curl "http://127.0.0.1:7437/observations/recent?project=engram&limit=5"
```

```json theme={null}
[
  {
    "id": 42,
    "session_id": "session-abc123",
    "type": "bug",
    "title": "Auth token expiration issue",
    "content": "Users were experiencing unexpected logouts...",
    "tool_name": null,
    "project": "engram",
    "scope": "project",
    "topic_key": null,
    "revision_count": 1,
    "duplicate_count": 1,
    "last_seen_at": null,
    "created_at": "2024-03-15 14:30:00",
    "updated_at": "2024-03-15 14:30:00",
    "deleted_at": null
  }
]
```

## Get Single Observation

`GET /observations/{id}`

Retrieve a specific observation by ID.

### Path Parameters

<ParamField path="id" type="integer" required>
  Observation ID
</ParamField>

### Response

Returns a single observation object (see schema below).

### Example

```bash theme={null}
curl http://127.0.0.1:7437/observations/42
```

## Update Observation

`PATCH /observations/{id}`

Update an existing observation. All fields are optional.

### Path Parameters

<ParamField path="id" type="integer" required>
  Observation ID to update
</ParamField>

### Request Body

<ParamField body="type" type="string">
  New observation type
</ParamField>

<ParamField body="title" type="string">
  New title
</ParamField>

<ParamField body="content" type="string">
  New content
</ParamField>

<ParamField body="project" type="string">
  New project name
</ParamField>

<ParamField body="scope" type="string">
  New scope
</ParamField>

<ParamField body="topic_key" type="string">
  New topic key
</ParamField>

### Response

Returns the updated observation object.

### Example

```bash theme={null}
curl -X PATCH http://127.0.0.1:7437/observations/42 \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Auth token expiration fix",
    "type": "fix"
  }'
```

## Delete Observation

`DELETE /observations/{id}`

Soft-delete or permanently delete an observation.

### Path Parameters

<ParamField path="id" type="integer" required>
  Observation ID to delete
</ParamField>

### Query Parameters

<ParamField query="hard" type="boolean" default="false">
  If `true`, permanently deletes. If `false`, soft-deletes (sets `deleted_at`)
</ParamField>

### Response

<ResponseField name="id" type="integer">
  The observation ID
</ResponseField>

<ResponseField name="status" type="string">
  Always "deleted"
</ResponseField>

<ResponseField name="hard_delete" type="boolean">
  Whether this was a permanent deletion
</ResponseField>

### Example

```bash theme={null}
# Soft delete
curl -X DELETE http://127.0.0.1:7437/observations/42

# Hard delete
curl -X DELETE "http://127.0.0.1:7437/observations/42?hard=true"
```

```json theme={null}
{
  "id": 42,
  "status": "deleted",
  "hard_delete": false
}
```

## Get Timeline

`GET /timeline`

Get chronological context around a specific observation.

### Query Parameters

<ParamField query="observation_id" type="integer" required>
  The anchor observation ID
</ParamField>

<ParamField query="before" type="integer" default="5">
  Number of observations to retrieve before the anchor
</ParamField>

<ParamField query="after" type="integer" default="5">
  Number of observations to retrieve after the anchor
</ParamField>

### Response

<ResponseField name="focus" type="object">
  The anchor observation
</ResponseField>

<ResponseField name="before" type="array">
  Observations before the anchor (chronological order)
</ResponseField>

<ResponseField name="after" type="array">
  Observations after the anchor (chronological order)
</ResponseField>

<ResponseField name="session_info" type="object">
  Session containing the anchor observation
</ResponseField>

<ResponseField name="total_in_range" type="integer">
  Total observations in the session
</ResponseField>

### Example

```bash theme={null}
curl "http://127.0.0.1:7437/timeline?observation_id=42&before=3&after=3"
```

```json theme={null}
{
  "focus": {
    "id": 42,
    "session_id": "session-abc123",
    "type": "bug",
    "title": "Auth token expiration issue",
    "content": "Users were experiencing unexpected logouts...",
    "created_at": "2024-03-15 14:30:00"
  },
  "before": [
    {
      "id": 39,
      "title": "Implemented refresh token rotation",
      "is_focus": false
    }
  ],
  "after": [
    {
      "id": 43,
      "title": "Added token expiration tests",
      "is_focus": false
    }
  ],
  "session_info": {
    "id": "session-abc123",
    "project": "engram",
    "started_at": "2024-03-15 14:00:00"
  },
  "total_in_range": 15
}
```

## Observation Schema

Complete observation object structure:

```typescript theme={null}
interface Observation {
  id: number;
  session_id: string;
  type: string;
  title: string;
  content: string;
  tool_name?: string | null;
  project?: string | null;
  scope: string;  // "project" | "personal"
  topic_key?: string | null;
  revision_count: number;
  duplicate_count: number;
  last_seen_at?: string | null;  // ISO 8601
  created_at: string;  // ISO 8601
  updated_at: string;  // ISO 8601
  deleted_at?: string | null;  // ISO 8601
}
```

## Deduplication

Engram automatically deduplicates observations within a 15-minute window using:

1. **Content hash** - Normalized content comparison
2. **Topic key** - Same topic = update existing observation
3. **Duplicate tracking** - Increments `duplicate_count` and updates `last_seen_at`

This prevents noise from repeated observations while preserving evidence of recurrence.
