Skip to content

Admin Operations

Administrative endpoints require elevated privileges (ADMIN_API_TOKEN) for managing enrichment processing, embedding generation, and entity cleanup. These operations are intended for maintenance, debugging, and bulk data operations.

For standard memory operations (store, recall, update, delete), see Memory Operations. For consolidation scheduling, see Consolidation Operations.


Most privileged admin operations require dual authentication:

  1. Standard API Token (AUTOMEM_API_TOKEN) — Required by the global API-token guard except for /health, /backup, /viewer*, and OPTIONS requests
  2. Admin Token (ADMIN_API_TOKEN) — Additional token for privileged operations

/backup is the admin-only exception: it bypasses the API-token guard but still requires the admin token.

Token TypeHeader MethodsQuery ParameterEnvironment Variable
API TokenAuthorization: Bearer <token> / X-API-Key: <token>?api_key=<token>AUTOMEM_API_TOKEN
Admin TokenX-Admin-Token: <token> / X-Admin-Api-Key: <token>?admin_token=<token>ADMIN_API_TOKEN
graph TB
    subgraph "Authentication Layers"
        Layer1["Layer 1: API Token<br/>AUTOMEM_API_TOKEN"]
        Layer2["Layer 2: Admin Token<br/>ADMIN_API_TOKEN<br/>Guards privileged operations"]
    end

    subgraph "Protected Operations"
        Reprocess["/enrichment/reprocess"]
        Reembed["/admin/reembed"]
        Sync["/admin/sync"]
        Backup["/backup"]
        EntityAudit["/entities/audit"]
        EntityMerge["/entity/&lt;slug&gt;/merge"]
    end

    subgraph "Exempt from API-token guard"
        Health["/health"]
        Viewer["/viewer*"]
        Options["OPTIONS"]
    end

    subgraph "API Token Required (when AUTOMEM_API_TOKEN configured)"
        Status["/enrichment/status"]
    end

    Layer1-->Status
    Layer1-->Reprocess
    Layer1-->Reembed
    Layer1-->Sync
    Layer1-->EntityAudit
    Layer1-->EntityMerge
    Layer2-->Reprocess
    Layer2-->Reembed
    Layer2-->Sync
    Layer2-->Backup
    Layer2-->EntityAudit
    Layer2-->EntityMerge

Authentication failures and other errors raised through Flask’s HTTP exception path use this envelope. Endpoint-specific failures can return their own response bodies, so do not assume it applies to every error response.

Status CodeResponseMeaning
401{"status": "error", "code": 401, "message": "Unauthorized"}Missing or invalid AUTOMEM_API_TOKEN
401{"status": "error", "code": 401, "message": "Admin authorization required"}Missing or invalid ADMIN_API_TOKEN
403{"status": "error", "code": 403, "message": "Admin token not configured"}Server has no ADMIN_API_TOKEN environment variable set
503{"status": "error", "code": 503, "message": "Embedding provider is not configured"}Returned only when the batch-embedding callable is absent, not when no real provider is configured. The stock server (app.py) always binds that callable, so this 503 is effectively unreachable there: a missing or placeholder provider returns HTTP 200 complete with every memory in failed (see Phase 2 under POST /admin/reembed). /admin/sync checks only when some memories need embedding, so dry_run and already_synced responses are unaffected
503{"status": "error", "code": 503, "message": "Qdrant is not available - cannot store embeddings"}Qdrant is unavailable for /admin/reembed or /admin/sync. /admin/sync uses the shorter message Qdrant is not available
503{"status": "error", "code": 503, "message": "FalkorDB is unavailable"}FalkorDB is unavailable for /admin/reembed or /admin/sync

Authentication: API token (when AUTOMEM_API_TOKEN is configured)

Purpose: Monitor the enrichment pipeline’s health and processing statistics. Unlike /health, this endpoint is protected by the global API token guard whenever AUTOMEM_API_TOKEN is set.

{
"status": "running",
"queue_size": 3,
"pending": 2,
"inflight": 1,
"max_attempts": 3,
"stats": {},
"classification": {},
"circuit": {
"open": false,
"circuit_open_skips": 0,
"recoveries": 0
}
}
FieldTypeDescription
statusstring"running" if enrichment worker is active, "stopped" if worker thread is dead
queue_sizeintegerCurrent number of jobs in the enrichment queue
pendingintegerCount of memories waiting to be processed
inflightintegerCount of memories currently being processed
max_attemptsintegerMaximum attempts for a forced job before giving up; non-forced jobs are not retried (from ENRICHMENT_MAX_ATTEMPTS)
statsobjectLifetime enrichment statistics (nested object with processing counters)
classificationobjectClassification service counters (LLM vs regex usage statistics)
circuitobjectLLM quota circuit-breaker state: open (true while the cooldown is running), circuit_open_skips (LLM calls skipped because the circuit was open or a recovery probe was already running), and recoveries (recovery probes that succeeded and closed the circuit)

The circuit breaker covers the LLM calls made for memory classification and auto-summarization. A quota error from the LLM opens it, and those calls are skipped for ENRICHMENT_CIRCUIT_COOLDOWN_SECONDS (default 300). After the cooldown one recovery probe call is allowed. A successful probe closes the circuit and increments recoveries; another quota error restarts the cooldown. A probe that fails with any other error, such as an unavailable OpenAI client, also closes the circuit but does not increment recoveries.

Terminal window
curl "https://your-automem-instance/enrichment/status" \
-H "Authorization: Bearer YOUR_API_TOKEN"
ObservationLikely CauseAction
status: "stopped"Worker thread crashedCheck application logs for exceptions, restart service
queue_size increasingWorker processing slower than intakeMonitor inflight, check for spaCy or OpenAI issues
High failure count in statsEnrichment logic errorsReview application logs, check Qdrant connectivity
inflight stuckWorker deadlockedRestart enrichment worker or service

Authentication: API token + Admin token

Purpose: Force re-enrichment of specific memories. Useful after:

  • Updating enrichment logic or configuration
  • Adding spaCy model capabilities
  • Fixing corrupted enrichment metadata
  • Recovering from systematic enrichment failures
ParameterTypeRequiredDescription
idsarray[string] | stringYesMemory UUIDs to reprocess. Provide a non-empty array or comma-separated string in the JSON body; ?ids= is used when the body has no ids value.

The handler trims and deduplicates IDs before queueing each one with forced=True, so repeated IDs in a request collapse to one queued ID and an already pending or in-flight ID can be requeued.

Terminal window
curl -X POST https://your-automem-instance/enrichment/reprocess \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "X-Admin-Token: YOUR_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ids": [
"a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"b2c3d4e5-f6a7-8901-bcde-f12345678901"
]
}'
FieldTypeDescription
statusstringAlways "queued"
countintegerNumber of distinct UUIDs queued; not a success/failure count
idsarray[string]Deduplicated memory UUIDs queued, sorted lexicographically
{
"status": "queued",
"count": 2,
"ids": [
"a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"b2c3d4e5-f6a7-8901-bcde-f12345678901"
]
}
graph TB
    Request["POST /enrichment/reprocess<br/>{ids: [...]}"]
    Auth["require_admin_token()"]
    Validate["Validate Request"]

    subgraph "Queueing Loop"
        Enqueue["enqueue_enrichment()<br/>with forced=True"]
        IncrQueued["queued_count++"]
    end

    Response["Return 202:<br/>status, count, ids"]

    Request-->Auth
    Auth-->Validate
    Validate-->Enqueue
    Enqueue-->IncrQueued
    IncrQueued-->Response

The reprocessing operation performs these steps:

  1. Validation Phase — Validates that the ids array is non-empty
  2. Queueing — Calls enqueue_enrichment(memory_id, forced=True) which:
    • Acquires state.enrichment_lock
    • Adds memory ID to state.enrichment_pending
    • Puts an EnrichmentJob with forced=True in queue
  3. Background Processing — The enrichment_worker() thread picks up jobs and calls enrich_memory() which:
    • Extracts entities via spaCy (if installed)
    • Creates temporal PRECEDED_BY edges
    • Finds semantic neighbors via Qdrant
    • Creates SIMILAR_TO relationships
    • Detects patterns and creates EXEMPLIFIES edges
    • Updates metadata.enriched_at timestamp

Authentication: API token + Admin token

Purpose: Regenerate embeddings for all memories in batches. Critical for:

  • Migrating to a different embedding model
  • Recovering from Qdrant data loss
  • Fixing corrupted embeddings
  • Bulk embedding generation after initial import
ParameterTypeRequiredDescription
batch_sizeintegerNoMemories per embedding-provider batch call. Default: 32. Max: 100
limitintegerNoMax memories to process. If omitted, processes all memories in database
forcebooleanNoOmits the WHERE m.content IS NOT NULL filter from the enumeration query. The endpoint never checks Qdrant for existing vectors, so every selected memory is re-embedded with or without force. Rows with null or empty content are still skipped, so without limit it selects the same memories as false. With limit, rows with null content count toward limit before they are skipped. Default: false
Terminal window
curl -X POST https://your-automem-instance/admin/reembed \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "X-Admin-Token: YOUR_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"batch_size": 32}'
graph TB
    Request["POST /admin/reembed<br/>{batch_size: 32, limit: 1000}"]
    Auth["require_admin_token()"]
    Init["Resolve configured embedding provider<br/>generate_real_embeddings_batch()"]

    FetchAll["Single FalkorDB query:<br/>MATCH (m:Memory)<br/>[WHERE m.content IS NOT NULL]<br/>RETURN id, content, tags, …<br/>ORDER BY timestamp DESC<br/>[LIMIT limit if set]"]

    subgraph "Batch Processing Loop"
        Slice["Slice next batch_size memories"]

        CallProvider["Embedding provider batch:<br/>generate_real_embeddings_batch()<br/>allow_placeholder_fallback=false"]

        UpdateQdrant["Qdrant: upsert()<br/>PointStruct with full payload<br/>(metadata_preserved=True)"]

        IncrCount["processed += len(points)<br/>(a failed batch adds to failed instead)"]
    end

    Response["Return summary:<br/>status, processed, failed,<br/>total, batch_size,<br/>metadata_preserved<br/>[failed_ids if failed&gt;0]"]

    Request-->Auth
    Auth-->Init
    Init-->FetchAll
    FetchAll-->Slice
    Slice-->CallProvider
    CallProvider-->UpdateQdrant
    UpdateQdrant-->IncrCount
    IncrCount-->|More batches?|Slice
    IncrCount-->|Done|Response
FieldTypeDescription
statusstringResult status
processedintegerNumber of memories successfully re-embedded
failedintegerNumber of memories that failed re-embedding
totalintegerNumber of memories selected for re-embedding: rows returned by the query (up to limit) that have non-empty content
batch_sizeintegerBatch size used (from request or default 32)
metadata_preservedbooleanWhether existing metadata was preserved during re-embedding
failed_idsarray[string](Conditional) Up to 10 memory IDs that failed re-embedding; only present when failed > 0
failed_ids_truncatedboolean(Conditional) true when more than 10 IDs failed and failed_ids was cut to the first 10; omitted otherwise
{
"status": "complete",
"processed": 1000,
"failed": 0,
"total": 1000,
"batch_size": 32,
"metadata_preserved": true
}

When no memory has content to embed, the endpoint returns early with a smaller response. failed, batch_size, and metadata_preserved are absent:

{
"status": "complete",
"message": "No memories found to reembed",
"processed": 0,
"total": 0
}

Phase 1: Memory Enumeration and Content Fetch

Fetches all memory data (or up to limit) in a single FalkorDB query:

MATCH (m:Memory)
WHERE m.content IS NOT NULL
RETURN m.id AS id, m.content AS content, m.tags AS tags,
m.importance AS importance, m.timestamp AS timestamp,
m.type AS type, m.confidence AS confidence,
m.metadata AS metadata, m.updated_at AS updated_at,
m.last_accessed AS last_accessed
ORDER BY m.timestamp DESC
-- When the `limit` request parameter is set, a LIMIT clause is appended after this line

When force=true, the WHERE m.content IS NOT NULL filter is omitted from the query. However, the Python collection loop still checks if content: before adding a row to the processing list, so memories with null or empty content are excluded regardless of force. There is no separate per-batch content retrieval step — all memory data is loaded upfront.

Phase 2: Embedding Provider Batch Generation

Tries one provider batch call per batch via generate_real_embeddings_batch(..., allow_placeholder_fallback=False). If that call raises, or returns the wrong number of vectors or the wrong dimensions, the helper discards the result and retries the batch one item at a time with single-input calls, never substituting placeholder vectors. With a real provider, the batch fails only if one of those item calls fails. With no provider, or with the placeholder provider (which the default EMBEDDING_PROVIDER=auto falls back to when no real provider initializes), the helper raises before any provider call, so there is no retry and every batch fails immediately. Uses whichever provider is configured (EMBEDDING_PROVIDER, Voyage, OpenAI, Ollama, FastEmbed, etc.) — not hard-coded to OpenAI. Returns 503 only when the embedding callable is absent or Qdrant/FalkorDB is unavailable before processing starts. Placeholder-only or other provider failures raised during batch generation are caught per batch and surfaced as HTTP 200 with status: "complete", failed, and failed_ids — check those fields rather than HTTP status alone. Dimension follows VECTOR_SIZE (default 1024).

Phase 3: Qdrant Update

Embeddings are written to Qdrant only. A failed Qdrant write is logged and counts the whole batch as failed, but later batches still run. FalkorDB graph data is not modified by this operation.

Batch SizeProvider API Calls (1000 memories)Approx TimeCost (1000 memories, OpenAI small)
10100~5 minutes~$0.06
3232~2 minutes~$0.06
5020~1 minute~$0.06
10010~30 seconds~$0.06

Recommendations:

  • Default 32 balances API call overhead and failure blast radius
  • Use 100 for large migrations (>10K memories) with stable provider access
  • Use 10 during testing or with rate-limited provider keys
  • Monitor processed count to detect stalls mid-operation

The operation continues with later batches even if an individual batch fails. With a real provider, the embedding helper first retries a failed batch call one item at a time with no placeholder vectors, and on the provider side the batch fails only if one of those item calls fails. With no provider or the placeholder provider, the helper raises before any call, so every batch fails immediately with no retry, and the response is still HTTP 200 complete with every memory counted in failed. Embedding generation and the Qdrant write share one try/except per batch, so a provider failure that remains (a failed item call, or the immediate failure above) or a failed Qdrant write marks the whole batch failed. Every memory in it is counted in failed, and its ID goes into failed_ids (capped at 10 in the response):

ErrorCauseBehavior
Provider API rate limitExceeded quotaLogged; the helper retries the batch item by item, and the whole batch is counted in failed only if one of those item calls fails (provider SDK may retry internally)
Wrong vector count or dimensionsProvider batch call returned a different number of vectors than inputs, or vectors of the wrong sizeHelper discards the batch result and retries item by item; whole batch counted in failed only if an item call fails
Qdrant connection failureNetwork issue or Qdrant downLogged; whole batch counted in failed, remaining batches still run (FalkorDB is never modified by this operation)
Null or empty contentMemory has no contentFiltered out silently during enumeration; never reaches a batch and is not counted in failed

A failed batch is logged as:

logger.error(f"Failed to process batch starting at index {i}: {e}")

Authentication: Admin token only

Purpose: Export a restore-compatible snapshot of FalkorDB and/or Qdrant as a single gzipped archive. Useful for scheduled backups and pre-migration safety snapshots.

Unlike the other admin endpoints, /backup is admin-only and does not accept the regular AUTOMEM_API_TOKEN by itself — it requires X-Admin-Token (or X-Admin-Api-Key).

ParameterTypeRequiredDescription
includestring (query)NoComma-separated stores to export: falkordb,qdrant. Defaults to both. Use include=falkordb or include=qdrant for a partial export
Terminal window
curl -H "X-Admin-Token: $ADMIN_API_TOKEN" \
"https://your-automem-instance/backup?include=falkordb,qdrant" \
-o automem-backup.tar.gz

Returns a binary application/gzip attachment named automem-backup-<timestamp>.tar.gz. The archive is restore-compatible and contains:

  • falkordb/falkordb_<timestamp>.json.gz (when FalkorDB is included)
  • qdrant/qdrant_<timestamp>.json.gz (when Qdrant is included)

Authentication: API token + Admin token

Purpose: Perform non-destructive drift repair between FalkorDB and Qdrant. Detects and reconciles discrepancies without deleting data. Memory IDs are compared excluding RECALL_EXCLUDED_TYPES (default MetaPattern) so internal consolidation artifacts do not count as drift.

ParameterTypeRequiredDescription
batch_sizeintegerNoNumber of memories to process per batch. Default: 32. Max: 100
dry_runbooleanNoIf true, report drift without making changes. Default: false

A dry_run returns {"status": "dry_run", "falkordb_count", "qdrant_count", "missing_count", "orphaned_count", "missing_sample", "orphaned_sample"}, where each sample holds up to 10 IDs. When dry_run is false and nothing is missing from Qdrant, the endpoint returns {"status": "already_synced", "falkordb_count", "qdrant_count", "synced": 0, "orphaned_count"} instead. Both of these return before the embedding-provider check, so they work without a provider.

When dry_run is false and memories are missing from Qdrant, the endpoint embeds them in batches and returns HTTP 200 with {"status": "complete", "falkordb_count", "qdrant_count", "synced", "failed", "orphaned_count"}. synced counts memories written to Qdrant, failed counts memories in batches that failed, and qdrant_count is the Qdrant count from before the run plus synced. failed_ids (up to 10 IDs) is present only when failed > 0, and failed_ids_truncated is true when more than 10 IDs failed. Missing memories with empty content are skipped without being counted in synced or failed, so those two can add up to less than the missing_count of a dry_run.

{
"status": "complete",
"falkordb_count": 1200,
"qdrant_count": 1200,
"synced": 40,
"failed": 0,
"orphaned_count": 0
}

A 503 is returned when Qdrant or FalkorDB is unavailable, or when memories need embedding and no batch-embedding callable is bound. See Error Responses.

Terminal window
curl -X POST https://your-automem-instance/admin/sync \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "X-Admin-Token: YOUR_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"batch_size": 32, "dry_run": false}'

Authentication: API token + Admin token

Purpose: Dry-run audit of entity cleanup. It reads the entity: tags on memories, reports which tags entity-tag validation accepts or rejects, and lists entity merge candidates. It changes no data, and the response always has "dry_run": true.

The read routes GET /entities, GET /entity/<slug>, and GET /entities/merge-candidates need only the API token.

All parameters are query parameters.

ParameterTypeRequiredDescription
summarybooleanNo1, true, or yes returns accepted_sample and rejected_sample in place of accepted and rejected_entities, and skips the merge-candidate search unless include_merge_candidates is also set
include_merge_candidatesbooleanNoSame truthy values. Only matters when summary is set: with summary and without this flag, the merge-candidate search is skipped. When summary is not set, the search always runs
limitintegerNoPage size for the accepted and rejected lists. Default: 100. Max: 500
offsetintegerNoEntries to skip in each list. Default: 0
categorystringNoKeep only entries in this entity category
reasonstringNoKeep only rejected entries with this rejection reason; accepted entries are omitted
FieldTypeDescription
statusstring"success"
dry_runbooleanAlways true
acceptedarray[object]Accepted entities, one per canonical tag and sorted by id, then paged by limit and offset. Each has id, category, slug, name, source_tags, references, and confidence. Returned as accepted_sample when summary is set
rejected_entitiesarray[object]Rejected tag occurrences, each with tag, memory_id, category, slug, and reason, paged the same way. Returned as rejected_sample when summary is set
merge_candidatesarray[object]Entity pairs from the merge-candidate search, each with entity_a, entity_b, canonical, alias, confidence, reason, and auto_merge. Empty when merge_candidates_skipped is true
countsobjectaccepted_entities, rejected_entities, accepted_total_unfiltered, rejected_total_unfiltered, merge_candidates, auto_merge_candidates, and review_merge_candidates
counts_by_reasonobjectRejected-tag counts keyed by rejection reason
counts_by_categoryobjectaccepted and rejected counts for each category
limit, offsetintegerThe paging values used; limit is capped at 500
summarybooleanWhether summary mode was used
merge_candidates_skippedbooleantrue when summary is set without include_merge_candidates

A 503 with the standard envelope is returned when FalkorDB is unavailable. Failures while auditing return HTTP 500 with {"error": "Failed to audit entities", "details": "<exception text>"} instead of the standard envelope. A non-integer limit or offset takes this path.

Terminal window
curl "https://your-automem-instance/entities/audit?summary=true&limit=20" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "X-Admin-Token: YOUR_ADMIN_TOKEN"

Authentication: API token + Admin token

Purpose: Merge one entity into another. <slug> in the path is the entity that gets merged away (the alias), and merge_into is the entity that is kept (the canonical). The merge moves the alias’s REFERENCED_IN edges to the canonical entity, adds the alias slug and the alias’s existing aliases to the canonical entity’s aliases, and sets merged_into on the alias so it no longer appears in entity listings.

ParameterTypeRequiredDescription
merge_intostring (JSON body)YesSlug of the entity to keep. Both slugs are matched exactly against entities that have not been merged; aliases are not resolved here, unlike GET /entity/<slug>
Terminal window
curl -X POST https://your-automem-instance/entity/acme-inc/merge \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "X-Admin-Token: YOUR_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"merge_into": "acme"}'

The response is {"status": "success", "merge": {...}}, where merge has these fields:

FieldTypeDescription
canonicalstringID of the entity that was kept
aliasstringID of the entity that was merged away
alias_slugstringSlug of the entity that was merged away
edges_movedintegerNumber of the alias’s REFERENCED_IN edges moved, including edges to memories the canonical entity already referenced (no duplicate edge is created)

These use the standard error envelope:

Status CodeMessageCause
400'merge_into' slug is requiredThe JSON body has no merge_into
400Source and target entity must be differentBoth slugs resolve to the same entity
404Entity '<slug>' not foundNo unmerged entity has the path slug
404Target entity '<merge_into>' not foundNo unmerged entity has the merge_into slug
503FalkorDB is unavailableFalkorDB is unavailable

A failure during the merge itself returns HTTP 500 with {"error": "Merge failed", "details": "<exception text>"} instead of the standard envelope.


Admin operations can:

  • Force expensive embedding-provider API calls (re-embedding entire database)
  • Trigger resource-intensive enrichment reprocessing
  • Access operational metrics (enrichment statistics)

Without admin token protection, a compromised API token could:

  1. Generate significant embedding-provider costs via repeated re-embedding
  2. Overload enrichment workers with duplicate jobs
  3. Enumerate all memory IDs via reprocess endpoint
PracticeRationaleImplementation
Separate tokensLimits blast radius of API token compromiseUse different values for AUTOMEM_API_TOKEN and ADMIN_API_TOKEN
Rotate periodicallyReduces window of exposureRegenerate tokens monthly, update all clients
Restrict admin accessMinimize privilege escalation riskShare admin token only with operations team
Use headers, not query paramsPrevents token leakage in logsPrefer Authorization: Bearer and X-Admin-Token headers
Monitor admin operationsDetect anomalous usageAlert on high-frequency /admin/reembed calls
Audit admin callsForensic capabilityLog admin operations with IP, timestamp, token hash

After spaCy model upgrades or enrichment logic changes:

Terminal window
# 1. Get all memory IDs that need reprocessing
MEMORY_IDS=$(curl -s "https://your-instance/recall?limit=100&query=*" \
-H "Authorization: Bearer $TOKEN" | jq -r '[.results[].memory.memory_id]')
# 2. Reprocess them
curl -X POST https://your-instance/enrichment/reprocess \
-H "Authorization: Bearer $TOKEN" \
-H "X-Admin-Token: $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"ids\": $MEMORY_IDS}"

When switching from text-embedding-3-small (1024-d, set by VECTOR_SIZE) to text-embedding-3-large (3072-d):

  1. Update VECTOR_SIZE environment variable
  2. Recreate the Qdrant collection with new dimensions
  3. Run /admin/reembed with batch_size=50 to regenerate all embeddings

If Qdrant data is corrupted or lost but FalkorDB is intact:

Terminal window
curl -X POST https://your-automem-instance/admin/reembed \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "X-Admin-Token: YOUR_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"batch_size": 50}'

See Operations / Health for complete recovery procedures.