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

# Apply Expiry Policy

> Sweep the agent's memories and expire everything the policy matches.

## Overview

Runs the expiry sweep. This is what moves memories from `active` to `expired` —
saving a policy on its own changes nothing.

Pass `dry_run: true` to get the identical report with **nothing written**, which
is how you preview a policy before committing to it.

## Authentication

API clients do not send an API key or `Authorization` header.

<ParamField header="X-Session-Token" type="string" required>
  Session token from [Activate Agent](/api-reference/sessions/activate-agent). Must match `agent_id`.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  Must be `application/json`
</ParamField>

## Path Parameters

<ParamField path="agent_id" type="string" required>
  The unique identifier of the agent.
</ParamField>

## Body

<ParamField body="dry_run" type="boolean" default="false">
  When `true`, report exactly what would be expired without writing anything.
  The body may be omitted entirely to accept the default.
</ParamField>

## Response Fields

<ResponseField name="scanned" type="integer">
  Active memories evaluated. A sweep never considers already-expired memories.
</ResponseField>

<ResponseField name="already_expired" type="integer">
  Expired memories not rescanned. Reported so a `matched: 0` result can be told
  apart from an empty namespace — these are governed by
  [purging](/api-reference/policy/purge-expired), not by the retention table.
</ResponseField>

<ResponseField name="matched" type="integer">
  Memories the policy matched.
</ResponseField>

<ResponseField name="expired" type="integer">
  Memories actually stamped. Always `0` on a dry run.
</ResponseField>

<ResponseField name="per_rule" type="object">
  Match counts keyed by the rule that fired (`retention.<type>` for table entries).
</ResponseField>

<ResponseField name="policy_is_empty" type="boolean">
  `true` when no policy is set, in which case the sweep is a no-op.
</ResponseField>

<RequestExample>
  ```bash cURL (dry run) theme={null}
  curl -X POST "http://localhost:8000/api/v2/agents/my-agent/policy/apply" \
    -H "X-Session-Token: your_session_token" \
    -H "Content-Type: application/json" \
    -d '{"dry_run": true}'
  ```

  ```bash cURL (apply) theme={null}
  curl -X POST "http://localhost:8000/api/v2/agents/my-agent/policy/apply" \
    -H "X-Session-Token: your_session_token" \
    -H "Content-Type: application/json" \
    -d '{"dry_run": false}'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 - OK theme={null}
  {
    "agent_id": "my-agent",
    "dry_run": false,
    "policy_is_empty": false,
    "scanned": 5,
    "already_expired": 3,
    "matched": 1,
    "expired": 1,
    "per_rule": { "retention.context": 1 },
    "memories": [
      {
        "id": "ec83b90a-a0a1-4c9a-9b4e-a8794b060612",
        "title": "Current sprint is the billing rewrite",
        "type": "context",
        "created_at": "2026-08-18T19:44:00+00:00",
        "updated_at": "2026-08-18T19:44:00+00:00",
        "expired_by": "retention.context"
      }
    ],
    "errors": [],
    "evaluated_at": "2026-08-18T19:56:29.873259+00:00"
  }
  ```

  ```json 200 - OK (no policy set) theme={null}
  {
    "agent_id": "my-agent",
    "dry_run": true,
    "policy_is_empty": true,
    "scanned": 4,
    "already_expired": 0,
    "matched": 0,
    "expired": 0,
    "per_rule": {},
    "memories": [],
    "errors": [],
    "evaluated_at": "2026-08-18T19:56:29.873259+00:00"
  }
  ```

  ```json 401 - Unauthorized (Missing Token) theme={null}
  {
    "detail": "Missing session token. Use X-Session-Token header."
  }
  ```
</ResponseExample>

## Notes

* Every memory in one sweep is stamped with the same `expired_at` (`evaluated_at`), so a batch is consistent.
* Memory age is measured from `updated_at`, falling back to `created_at` — editing a memory resets its clock.
* A memory with no usable timestamp is never expired.
* Per-memory failures are collected in `errors` rather than aborting the sweep.
* The nightly [schedule](/cli/schedule/enable) job calls this endpoint's logic automatically.

## Next Steps

* [Purge Expired Memories](/api-reference/policy/purge-expired) for the destructive step
* [Restore Memory](/api-reference/data/restore-memory) to undo one expiry
