---
title: "Add Memory Batch V1"
method: POST
path: "/v1/memory/batch"
tags: ["v1", "Memory"]
---

# Add Memory Batch V1

`POST /v1/memory/batch`

Add multiple memory items in a batch with size validation and background processing.
    
    **Authentication Required**:
    One of the following authentication methods must be used:
    - Bearer token in `Authorization` header
    - API Key in `X-API-Key` header
    - Session token in `X-Session-Token` header
    
    **Required Headers**:
    - Content-Type: application/json
    - X-Client-Type: (e.g., 'papr_plugin', 'browser_extension')
    
    The API validates individual memory content size against MAX_CONTENT_LENGTH environment variable (defaults to 15000 bytes).

## Query parameters

- `skip_background_processing` boolean — If True, skips adding background tasks for processing
- `enable_holographic` boolean — If True, applies holographic neural transforms and stores in holographic collection
- `frequency_schema_id` string, nullable — Frequency schema for holographic embedding (e.g. 'cosqa', 'scifact'). Required when enable_holographic=True. Call GET /v1/frequencies to see available schemas.

## Request body

- BatchMemoryRequest — Request model for batch adding memories
  - `policy` MemoryAddPolicy — Policy for add / batch / document / message ingestion.
    - `consent` 'explicit' | 'implicit' | 'terms' | 'none' — How the data owner allowed this memory to be stored/used. Aligned with Open Memory Object (OMO) standard.
    - `risk` 'none' | 'sensitive' | 'flagged' — Post-ingest safety assessment of memory content. Aligned with Open Memory Object (OMO) standard.
    - `acl` ACLConfig — Simplified Access Control List configuration. Aligned with Open Memory Object (OMO) standard. See: https://github.com/anthropics/open-memory-object **Supported Entity Prefixes:** | Prefix | Description | Validation | |--------|-------------|------------| | `user:` | Internal Papr user ID | Validated against Parse users | | `external_user:` | Your app's user ID | Not validated (your responsibility) | | `organization:` | Organization ID | Validated against your organizations | | `namespace:` | Namespace ID | Validated against your namespaces | | `workspace:` | Workspace ID | Validated against your workspaces | | `role:` | Parse role ID | Validated against your roles | **Examples:** ```python acl = ACLConfig( read=["external_user:alice_123", "organization:org_acme"], write=["external_user:alice_123"] ) ``` **Validation Rules:** - Internal entities (user, organization, namespace, workspace, role) are validated - External entities (external_user) are NOT validated - your app is responsible - Invalid internal entities will return an error - Unprefixed values default to `external_user:` for backwards compatibility
      - `read` string[] — Entity IDs that can read this memory. Format: 'prefix:id' (e.g., 'external_user:alice', 'organization:org_123'). Supported prefixes: user, external_user, organization, namespace, workspace, role. Unprefixed values treated as external_user for backwards compatibility.
      - `write` string[] — Entity IDs that can write/modify this memory. Format: 'prefix:id' (e.g., 'external_user:alice'). Supported prefixes: user, external_user, organization, namespace, workspace, role.
    - `transform_embedding` TransformEmbeddingPolicy
      - `mode` 'none' | 'auto' | 'manual'
      - `domain_id` string, nullable — Signal domain id or shorthand (e.g. cosqa)
      - `signals` object, nullable — BYO band text values when mode=manual
    - `graph` GraphPolicyBlock
      - `mode` 'none' | 'auto' | 'manual'
      - `schema_id` string, nullable
      - `link_to` union — Shorthand DSL for node/edge constraints under policy.graph. Not a separate graph mode — expands into node_constraints and edge_constraints at resolve time and merges with any explicit constraints in the same request. Default create policy is upsert (create if not found); use dict form with create='lookup' for link-only. Prefer over deprecated top-level link_to.
        - string
        - string[]
        - object
      - `node_constraints` NodeConstraintInput[], nullable — Full node constraint objects. Same rules as policy.graph.link_to after expansion; use link_to for compact DSL or this field for explicit control. Both may be set.
        - `node_type` string, nullable — Node type this constraint applies to (e.g., 'Task', 'Project', 'Person'). Optional at schema level (implicit from parent UserNodeType), required at memory level (in memory_policy.node_constraints).
        - `when` object, nullable — Condition for when this constraint applies. Supports logical operators: '_and', '_or', '_not'. Examples: Simple: {'priority': 'high'} - matches when priority equals 'high'. AND: {'_and': [{'priority': 'high'}, {'status': 'active'}]} - all must match. OR: {'_or': [{'status': 'active'}, {'status': 'pending'}]} - any must match. NOT: {'_not': {'status': 'completed'}} - negation. Complex: {'_and': [{'priority': 'high'}, {'_or': [{'status': 'active'}, {'urgent': true}]}]}
        - `create` 'upsert' | 'lookup' | 'auto' | 'never' — 'upsert': Create if not found via search (default). 'lookup': Only link to existing nodes (controlled vocabulary). Deprecated aliases: 'auto' -> 'upsert', 'never' -> 'lookup'.
        - `on_miss` 'create' | 'ignore' | 'error', nullable — Explicit behavior when no match found via search. 'create': create new node (same as upsert). 'ignore': skip node creation (same as lookup). 'error': raise error if node not found. If specified, overrides 'create' field.
        - `link_only` boolean — DEPRECATED: Use create='lookup' instead. Shorthand for create='lookup'. When True, only links to existing nodes (controlled vocabulary). Equivalent to @lookup decorator in schema definitions.
        - `search` SearchConfigInput — Configuration for finding/selecting existing nodes. Defines which properties to match on and how, in priority order. The first matching property wins. **String Shorthand** (simple cases - converts to exact match): SearchConfig(properties=["id", "email"]) # Equivalent to: SearchConfig(properties=[PropertyMatch.exact("id"), PropertyMatch.exact("email")]) **Mixed Form** (combine strings and PropertyMatch): SearchConfig(properties=[ "id", # String -> exact match PropertyMatch.semantic("title", 0.9) # Full control ]) **Full Form** (maximum control): SearchConfig(properties=[ PropertyMatch(name="id", mode="exact"), PropertyMatch(name="title", mode="semantic", threshold=0.85) ]) **To select a specific node by ID**: SearchConfig(properties=[PropertyMatch.exact("id", "TASK-123")])
          - `properties` PropertyMatch[], nullable — Properties to match on, in priority order (first match wins). Accepts strings (converted to exact match) or PropertyMatch objects. Use PropertyMatch with 'value' field for specific node selection.
            - `name` string, required — Property name to match on (e.g., 'id', 'email', 'title')
            - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
            - `threshold` number — Similarity threshold for semantic/fuzzy modes (0.0-1.0). Ignored for exact mode.
            - `value` unknown
          - `via_relationship` RelationshipMatchInput[], nullable — Search for nodes via their relationships. Example: Find tasks assigned to a specific person. Each RelationshipMatch specifies edge_type, target_type, and target_search. Multiple relationship matches are ANDed together.
            - `edge_type` string, required — The relationship type to traverse (e.g., 'ASSIGNED_TO', 'BELONGS_TO')
            - `target_type` string, required — The target node type at the end of the relationship (e.g., 'Person', 'Project')
            - `target_search` SearchConfigInput, required — recursive
            - `direction` 'outgoing' | 'incoming' — Direction of the relationship from the node being searched. 'outgoing': node --edge--> target (default). 'incoming': target --edge--> node.
          - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
          - `threshold` number — Default similarity threshold for semantic/fuzzy matching (0.0-1.0). Used when property doesn't specify its own threshold.
        - `set` object, nullable — Set property values on nodes. Supports: 1. Exact value: {'status': 'done'} - sets exact value. 2. Auto-extract: {'status': {'mode': 'auto'}} - LLM extracts from content. 3. Text mode: {'summary': {'mode': 'auto', 'text_mode': 'merge'}} - controls text updates. For text properties, text_mode can be 'replace', 'append', or 'merge'.
      - `edge_constraints` EdgeConstraintInput[], nullable — Full edge constraint objects. Same rules as edge entries in policy.graph.link_to after expansion; both may be set in the same request.
        - `edge_type` string, nullable — Edge/relationship type this constraint applies to (e.g., 'MITIGATES', 'ASSIGNED_TO'). Optional at schema level (implicit from parent UserRelationshipType), required at memory level (in memory_policy.edge_constraints).
        - `source_type` string, nullable — Filter: only apply when source node is of this type. Example: source_type='SecurityBehavior' - only applies to edges from SecurityBehavior nodes.
        - `target_type` string, nullable — Filter: only apply when target node is of this type. Example: target_type='TacticDef' - only applies to edges targeting TacticDef nodes.
        - `direction` 'outgoing' | 'incoming' | 'both' — Direction of edges this constraint applies to. 'outgoing': edges where current node is source (default). 'incoming': edges where current node is target. 'both': applies in either direction.
        - `when` object, nullable — Condition for when this constraint applies. Supports logical operators: '_and', '_or', '_not'. Applied to edge properties or context. Example: {'_and': [{'severity': 'high'}, {'_not': {'status': 'deprecated'}}]}
        - `create` 'upsert' | 'lookup' | 'auto' | 'never' — 'upsert': Create target node if not found via search (default). 'lookup': Only link to existing target nodes (controlled vocabulary). When 'lookup', edges to non-existing targets are skipped. Deprecated aliases: 'auto' -> 'upsert', 'never' -> 'lookup'.
        - `on_miss` 'create' | 'ignore' | 'error', nullable — Explicit behavior when no target match found via search. 'create': create new target node (same as upsert). 'ignore': skip edge creation (same as lookup). 'error': raise error if target not found. If specified, overrides 'create' field.
        - `link_only` boolean — DEPRECATED: Use create='lookup' instead. Shorthand for create='lookup'. When True, only links to existing target nodes. Equivalent to @lookup decorator in schema definitions.
        - `search` SearchConfigInput — Configuration for finding/selecting existing nodes. Defines which properties to match on and how, in priority order. The first matching property wins. **String Shorthand** (simple cases - converts to exact match): SearchConfig(properties=["id", "email"]) # Equivalent to: SearchConfig(properties=[PropertyMatch.exact("id"), PropertyMatch.exact("email")]) **Mixed Form** (combine strings and PropertyMatch): SearchConfig(properties=[ "id", # String -> exact match PropertyMatch.semantic("title", 0.9) # Full control ]) **Full Form** (maximum control): SearchConfig(properties=[ PropertyMatch(name="id", mode="exact"), PropertyMatch(name="title", mode="semantic", threshold=0.85) ]) **To select a specific node by ID**: SearchConfig(properties=[PropertyMatch.exact("id", "TASK-123")])
          - `properties` PropertyMatch[], nullable — Properties to match on, in priority order (first match wins). Accepts strings (converted to exact match) or PropertyMatch objects. Use PropertyMatch with 'value' field for specific node selection.
            - `name` string, required — Property name to match on (e.g., 'id', 'email', 'title')
            - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
            - `threshold` number — Similarity threshold for semantic/fuzzy modes (0.0-1.0). Ignored for exact mode.
            - `value` unknown
          - `via_relationship` RelationshipMatchInput[], nullable — Search for nodes via their relationships. Example: Find tasks assigned to a specific person. Each RelationshipMatch specifies edge_type, target_type, and target_search. Multiple relationship matches are ANDed together.
            - `edge_type` string, required — The relationship type to traverse (e.g., 'ASSIGNED_TO', 'BELONGS_TO')
            - `target_type` string, required — The target node type at the end of the relationship (e.g., 'Person', 'Project')
            - `target_search` SearchConfigInput, required — recursive
            - `direction` 'outgoing' | 'incoming' — Direction of the relationship from the node being searched. 'outgoing': node --edge--> target (default). 'incoming': target --edge--> node.
          - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
          - `threshold` number — Default similarity threshold for semantic/fuzzy matching (0.0-1.0). Used when property doesn't specify its own threshold.
        - `set` object, nullable — Set property values on edges. Supports: 1. Exact value: {'weight': 1.0} - sets exact value. 2. Auto-extract: {'reason': {'mode': 'auto'}} - LLM extracts from content. Edge properties are useful for relationship metadata (weight, timestamp, reason, etc.).
      - `nodes` NodeSpec[], nullable
        - `id` string, required — Unique identifier for this node
        - `type` string, required — Node type/label (e.g., 'Transaction', 'Product', 'Person')
        - `properties` object — Properties for this node
      - `relationships` RelationshipSpec[], nullable
        - `source` string, required — ID of the source node
        - `target` string, required — ID of the target node
        - `type` string, required — Relationship type (e.g., 'PURCHASED', 'WORKS_AT', 'ASSIGNED_TO')
        - `properties` object, nullable — Optional properties for this relationship
  - `memory_policy` object, nullable — Unified memory processing policy. This is the SINGLE source of truth for how a memory should be processed, combining graph generation control AND OMO (Open Memory Object) safety standards. **Graph Generation Modes:** - auto: LLM extracts entities freely (default) - manual: Developer provides exact nodes (no LLM extraction) **OMO Safety Standards:** - consent: How data owner allowed storage (explicit, implicit, terms, none) - risk: Safety assessment (none, sensitive, flagged) - acl: Access control list for read/write permissions **Schema Integration:** - schema_id: Reference a schema that may have its own default memory_policy - Schema-level policies are merged with request-level (request takes precedence)
    - `mode` 'auto' | 'manual' — Memory processing mode - describes WHO controls graph generation. - AUTO: LLM extracts entities freely (default) - MANUAL: Developer provides exact nodes (no LLM extraction) Note: 'structured' is accepted as a deprecated alias for 'manual'.
    - `nodes` NodeSpec[], nullable — For manual mode: Exact nodes to create (no LLM extraction). Required when mode='manual'. Each node needs id, type, and properties.
      - `id` string, required — Unique identifier for this node
      - `type` string, required — Node type/label (e.g., 'Transaction', 'Product', 'Person')
      - `properties` object — Properties for this node
    - `relationships` RelationshipSpec[], nullable — Relationships between nodes. Supports special placeholders: '$this' = the Memory node being created, '$previous' = the user's most recent memory. Examples: {source: '$this', target: '$previous', type: 'FOLLOWS'} links to previous memory. {source: '$this', target: 'mem_abc', type: 'REFERENCES'} links to specific memory.
      - `source` string, required — ID of the source node
      - `target` string, required — ID of the target node
      - `type` string, required — Relationship type (e.g., 'PURCHASED', 'WORKS_AT', 'ASSIGNED_TO')
      - `properties` object, nullable — Optional properties for this relationship
    - `node_constraints` NodeConstraintInput[], nullable — Rules for how LLM-extracted nodes should be created/updated. Used in 'auto' mode when present. Controls creation policy, property forcing, and merge behavior.
      - `node_type` string, nullable — Node type this constraint applies to (e.g., 'Task', 'Project', 'Person'). Optional at schema level (implicit from parent UserNodeType), required at memory level (in memory_policy.node_constraints).
      - `when` object, nullable — Condition for when this constraint applies. Supports logical operators: '_and', '_or', '_not'. Examples: Simple: {'priority': 'high'} - matches when priority equals 'high'. AND: {'_and': [{'priority': 'high'}, {'status': 'active'}]} - all must match. OR: {'_or': [{'status': 'active'}, {'status': 'pending'}]} - any must match. NOT: {'_not': {'status': 'completed'}} - negation. Complex: {'_and': [{'priority': 'high'}, {'_or': [{'status': 'active'}, {'urgent': true}]}]}
      - `create` 'upsert' | 'lookup' | 'auto' | 'never' — 'upsert': Create if not found via search (default). 'lookup': Only link to existing nodes (controlled vocabulary). Deprecated aliases: 'auto' -> 'upsert', 'never' -> 'lookup'.
      - `on_miss` 'create' | 'ignore' | 'error', nullable — Explicit behavior when no match found via search. 'create': create new node (same as upsert). 'ignore': skip node creation (same as lookup). 'error': raise error if node not found. If specified, overrides 'create' field.
      - `link_only` boolean — DEPRECATED: Use create='lookup' instead. Shorthand for create='lookup'. When True, only links to existing nodes (controlled vocabulary). Equivalent to @lookup decorator in schema definitions.
      - `search` SearchConfigInput — Configuration for finding/selecting existing nodes. Defines which properties to match on and how, in priority order. The first matching property wins. **String Shorthand** (simple cases - converts to exact match): SearchConfig(properties=["id", "email"]) # Equivalent to: SearchConfig(properties=[PropertyMatch.exact("id"), PropertyMatch.exact("email")]) **Mixed Form** (combine strings and PropertyMatch): SearchConfig(properties=[ "id", # String -> exact match PropertyMatch.semantic("title", 0.9) # Full control ]) **Full Form** (maximum control): SearchConfig(properties=[ PropertyMatch(name="id", mode="exact"), PropertyMatch(name="title", mode="semantic", threshold=0.85) ]) **To select a specific node by ID**: SearchConfig(properties=[PropertyMatch.exact("id", "TASK-123")])
        - `properties` PropertyMatch[], nullable — Properties to match on, in priority order (first match wins). Accepts strings (converted to exact match) or PropertyMatch objects. Use PropertyMatch with 'value' field for specific node selection.
          - `name` string, required — Property name to match on (e.g., 'id', 'email', 'title')
          - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
          - `threshold` number — Similarity threshold for semantic/fuzzy modes (0.0-1.0). Ignored for exact mode.
          - `value` unknown
        - `via_relationship` RelationshipMatchInput[], nullable — Search for nodes via their relationships. Example: Find tasks assigned to a specific person. Each RelationshipMatch specifies edge_type, target_type, and target_search. Multiple relationship matches are ANDed together.
          - `edge_type` string, required — The relationship type to traverse (e.g., 'ASSIGNED_TO', 'BELONGS_TO')
          - `target_type` string, required — The target node type at the end of the relationship (e.g., 'Person', 'Project')
          - `target_search` SearchConfigInput, required — recursive
          - `direction` 'outgoing' | 'incoming' — Direction of the relationship from the node being searched. 'outgoing': node --edge--> target (default). 'incoming': target --edge--> node.
        - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
        - `threshold` number — Default similarity threshold for semantic/fuzzy matching (0.0-1.0). Used when property doesn't specify its own threshold.
      - `set` object, nullable — Set property values on nodes. Supports: 1. Exact value: {'status': 'done'} - sets exact value. 2. Auto-extract: {'status': {'mode': 'auto'}} - LLM extracts from content. 3. Text mode: {'summary': {'mode': 'auto', 'text_mode': 'merge'}} - controls text updates. For text properties, text_mode can be 'replace', 'append', or 'merge'.
    - `edge_constraints` EdgeConstraintInput[], nullable — Rules for how LLM-extracted edges/relationships should be created/handled. Used in 'auto' mode when present. Controls: - create: 'auto' (create target if not found) or 'never' (controlled vocabulary) - search: How to find existing target nodes - set: Edge property values (exact or auto-extracted) - source_type/target_type: Filter by connected node types Example: {edge_type: 'MITIGATES', create: 'never', search: {properties: ['name']}}
      - `edge_type` string, nullable — Edge/relationship type this constraint applies to (e.g., 'MITIGATES', 'ASSIGNED_TO'). Optional at schema level (implicit from parent UserRelationshipType), required at memory level (in memory_policy.edge_constraints).
      - `source_type` string, nullable — Filter: only apply when source node is of this type. Example: source_type='SecurityBehavior' - only applies to edges from SecurityBehavior nodes.
      - `target_type` string, nullable — Filter: only apply when target node is of this type. Example: target_type='TacticDef' - only applies to edges targeting TacticDef nodes.
      - `direction` 'outgoing' | 'incoming' | 'both' — Direction of edges this constraint applies to. 'outgoing': edges where current node is source (default). 'incoming': edges where current node is target. 'both': applies in either direction.
      - `when` object, nullable — Condition for when this constraint applies. Supports logical operators: '_and', '_or', '_not'. Applied to edge properties or context. Example: {'_and': [{'severity': 'high'}, {'_not': {'status': 'deprecated'}}]}
      - `create` 'upsert' | 'lookup' | 'auto' | 'never' — 'upsert': Create target node if not found via search (default). 'lookup': Only link to existing target nodes (controlled vocabulary). When 'lookup', edges to non-existing targets are skipped. Deprecated aliases: 'auto' -> 'upsert', 'never' -> 'lookup'.
      - `on_miss` 'create' | 'ignore' | 'error', nullable — Explicit behavior when no target match found via search. 'create': create new target node (same as upsert). 'ignore': skip edge creation (same as lookup). 'error': raise error if target not found. If specified, overrides 'create' field.
      - `link_only` boolean — DEPRECATED: Use create='lookup' instead. Shorthand for create='lookup'. When True, only links to existing target nodes. Equivalent to @lookup decorator in schema definitions.
      - `search` SearchConfigInput — Configuration for finding/selecting existing nodes. Defines which properties to match on and how, in priority order. The first matching property wins. **String Shorthand** (simple cases - converts to exact match): SearchConfig(properties=["id", "email"]) # Equivalent to: SearchConfig(properties=[PropertyMatch.exact("id"), PropertyMatch.exact("email")]) **Mixed Form** (combine strings and PropertyMatch): SearchConfig(properties=[ "id", # String -> exact match PropertyMatch.semantic("title", 0.9) # Full control ]) **Full Form** (maximum control): SearchConfig(properties=[ PropertyMatch(name="id", mode="exact"), PropertyMatch(name="title", mode="semantic", threshold=0.85) ]) **To select a specific node by ID**: SearchConfig(properties=[PropertyMatch.exact("id", "TASK-123")])
        - `properties` PropertyMatch[], nullable — Properties to match on, in priority order (first match wins). Accepts strings (converted to exact match) or PropertyMatch objects. Use PropertyMatch with 'value' field for specific node selection.
          - `name` string, required — Property name to match on (e.g., 'id', 'email', 'title')
          - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
          - `threshold` number — Similarity threshold for semantic/fuzzy modes (0.0-1.0). Ignored for exact mode.
          - `value` unknown
        - `via_relationship` RelationshipMatchInput[], nullable — Search for nodes via their relationships. Example: Find tasks assigned to a specific person. Each RelationshipMatch specifies edge_type, target_type, and target_search. Multiple relationship matches are ANDed together.
          - `edge_type` string, required — The relationship type to traverse (e.g., 'ASSIGNED_TO', 'BELONGS_TO')
          - `target_type` string, required — The target node type at the end of the relationship (e.g., 'Person', 'Project')
          - `target_search` SearchConfigInput, required — recursive
          - `direction` 'outgoing' | 'incoming' — Direction of the relationship from the node being searched. 'outgoing': node --edge--> target (default). 'incoming': target --edge--> node.
        - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
        - `threshold` number — Default similarity threshold for semantic/fuzzy matching (0.0-1.0). Used when property doesn't specify its own threshold.
      - `set` object, nullable — Set property values on edges. Supports: 1. Exact value: {'weight': 1.0} - sets exact value. 2. Auto-extract: {'reason': {'mode': 'auto'}} - LLM extracts from content. Edge properties are useful for relationship metadata (weight, timestamp, reason, etc.).
    - `schema_id` string, nullable — Reference a UserGraphSchema by ID. The schema's memory_policy (if defined) will be used as defaults, with this request's settings taking precedence.
    - `consent` 'explicit' | 'implicit' | 'terms' | 'none' — How the data owner allowed this memory to be stored/used. Aligned with Open Memory Object (OMO) standard.
    - `risk` 'none' | 'sensitive' | 'flagged' — Post-ingest safety assessment of memory content. Aligned with Open Memory Object (OMO) standard.
    - `acl` ACLConfig — Simplified Access Control List configuration. Aligned with Open Memory Object (OMO) standard. See: https://github.com/anthropics/open-memory-object **Supported Entity Prefixes:** | Prefix | Description | Validation | |--------|-------------|------------| | `user:` | Internal Papr user ID | Validated against Parse users | | `external_user:` | Your app's user ID | Not validated (your responsibility) | | `organization:` | Organization ID | Validated against your organizations | | `namespace:` | Namespace ID | Validated against your namespaces | | `workspace:` | Workspace ID | Validated against your workspaces | | `role:` | Parse role ID | Validated against your roles | **Examples:** ```python acl = ACLConfig( read=["external_user:alice_123", "organization:org_acme"], write=["external_user:alice_123"] ) ``` **Validation Rules:** - Internal entities (user, organization, namespace, workspace, role) are validated - External entities (external_user) are NOT validated - your app is responsible - Invalid internal entities will return an error - Unprefixed values default to `external_user:` for backwards compatibility
      - `read` string[] — Entity IDs that can read this memory. Format: 'prefix:id' (e.g., 'external_user:alice', 'organization:org_123'). Supported prefixes: user, external_user, organization, namespace, workspace, role. Unprefixed values treated as external_user for backwards compatibility.
      - `write` string[] — Entity IDs that can write/modify this memory. Format: 'prefix:id' (e.g., 'external_user:alice'). Supported prefixes: user, external_user, organization, namespace, workspace, role.
  - `link_to` union — DEPRECATED: Use policy.graph.link_to instead. Shorthand DSL for node/edge constraints (same as node_constraints, compact syntax). Expands and merges into memory_policy.node_constraints and edge_constraints at resolve time. Default create is upsert; use dict form with create='lookup' (or legacy 'never') for link-only. Formats: - String: 'Task:title' (semantic match on Task.title, upsert by default) - List: ['Task:title', 'Person:email'] (multiple constraints) - Dict: {'Task:title': {'set': {...}, 'create': 'lookup'}} (full options) Syntax: - Node: 'Type:property', 'Type:prop=value' (exact), 'Type:prop~value' (semantic) - Edge: 'Source->EDGE->Target:property' (arrow syntax) - Via: 'Type.via(EDGE->Target:prop)' (relationship traversal) - Special: '$this', '$previous', '$context:N' Example lookup-only: {'SecurityPolicy:name': {'create': 'lookup'}}
    - string
    - string[]
    - object
  - `graph_generation` object, nullable — Graph generation configuration
    - `mode` 'auto' | 'manual' — Graph generation modes
    - `auto` AutoGraphGeneration — AI-powered graph generation with optional guidance
      - `schema_id` string, nullable — Force AI to use this specific schema instead of auto-selecting
      - `property_overrides` PropertyOverrideRule[], nullable — Override specific property values in AI-generated nodes with match conditions
        - `nodeLabel` string, required — Node type to apply overrides to (e.g., 'User', 'SecurityBehavior')
        - `match` object, nullable — Optional conditions that must be met for override to apply. If not provided, applies to all nodes of this type
        - `set` object, required — Properties to set/override on matching nodes
    - `manual` ManualGraphGeneration — Complete manual control over graph structure
      - `nodes` GraphOverrideNode[], required — Exact nodes to create
        - `id` string, required — **REQUIRED**: Unique identifier for this node. Must be unique within this request. Relationships reference this via source_node_id/target_node_id. Example: 'person_john_123', 'finding_cve_2024_1234'
        - `label` string, required — **REQUIRED**: Node type from your UserGraphSchema. View available types at GET /v1/schemas. System types: Memory, Person, Company, Project, Task, Insight, Meeting, Opportunity, Code
        - `properties` object, required — **REQUIRED**: Node properties matching your UserGraphSchema definition. Must include: (1) All required properties from your schema, (2) unique_identifiers if defined (e.g., 'email' for Person) to enable MERGE deduplication. View schema requirements at GET /v1/schemas
      - `relationships` GraphOverrideRelationship[] — Exact relationships to create
        - `source_node_id` string, required — **REQUIRED**: Must exactly match the 'id' field of a node defined in the 'nodes' array of this request
        - `target_node_id` string, required — **REQUIRED**: Must exactly match the 'id' field of a node defined in the 'nodes' array of this request
        - `relationship_type` string, required — **REQUIRED**: Relationship type from your UserGraphSchema. View available types at GET /v1/schemas. System types: WORKS_FOR, WORKS_ON, HAS_PARTICIPANT, DISCUSSES, MENTIONS, RELATES_TO, CREATED_BY
        - `properties` object, nullable — Optional relationship properties (e.g., {'since': '2024-01-01', 'role': 'manager'})
  - `external_user_id` string, nullable — Your application's user identifier for all memories in the batch. This is the primary way to identify users. Papr will automatically resolve or create internal users as needed.
  - `user_id` string, nullable — DEPRECATED: Use 'external_user_id' instead. Internal Papr Parse user ID.
  - `organization_id` string, nullable — DEPRECATED - Internal only. Auto-populated from API key scope. Do not set manually. The organization is resolved automatically from the API key's associated organization.
  - `namespace_id` string, nullable — Optional namespace ID for multi-tenant batch memory scoping. When provided, all memories in the batch are associated with this namespace.
  - `memories` AddMemoryRequest[], required — List of memory items to add in batch
    - `policy` MemoryAddPolicy — Policy for add / batch / document / message ingestion.
      - `consent` 'explicit' | 'implicit' | 'terms' | 'none' — How the data owner allowed this memory to be stored/used. Aligned with Open Memory Object (OMO) standard.
      - `risk` 'none' | 'sensitive' | 'flagged' — Post-ingest safety assessment of memory content. Aligned with Open Memory Object (OMO) standard.
      - `acl` ACLConfig — Simplified Access Control List configuration. Aligned with Open Memory Object (OMO) standard. See: https://github.com/anthropics/open-memory-object **Supported Entity Prefixes:** | Prefix | Description | Validation | |--------|-------------|------------| | `user:` | Internal Papr user ID | Validated against Parse users | | `external_user:` | Your app's user ID | Not validated (your responsibility) | | `organization:` | Organization ID | Validated against your organizations | | `namespace:` | Namespace ID | Validated against your namespaces | | `workspace:` | Workspace ID | Validated against your workspaces | | `role:` | Parse role ID | Validated against your roles | **Examples:** ```python acl = ACLConfig( read=["external_user:alice_123", "organization:org_acme"], write=["external_user:alice_123"] ) ``` **Validation Rules:** - Internal entities (user, organization, namespace, workspace, role) are validated - External entities (external_user) are NOT validated - your app is responsible - Invalid internal entities will return an error - Unprefixed values default to `external_user:` for backwards compatibility
        - `read` string[] — Entity IDs that can read this memory. Format: 'prefix:id' (e.g., 'external_user:alice', 'organization:org_123'). Supported prefixes: user, external_user, organization, namespace, workspace, role. Unprefixed values treated as external_user for backwards compatibility.
        - `write` string[] — Entity IDs that can write/modify this memory. Format: 'prefix:id' (e.g., 'external_user:alice'). Supported prefixes: user, external_user, organization, namespace, workspace, role.
      - `transform_embedding` TransformEmbeddingPolicy
        - `mode` 'none' | 'auto' | 'manual'
        - `domain_id` string, nullable — Signal domain id or shorthand (e.g. cosqa)
        - `signals` object, nullable — BYO band text values when mode=manual
      - `graph` GraphPolicyBlock
        - `mode` 'none' | 'auto' | 'manual'
        - `schema_id` string, nullable
        - `link_to` union — Shorthand DSL for node/edge constraints under policy.graph. Not a separate graph mode — expands into node_constraints and edge_constraints at resolve time and merges with any explicit constraints in the same request. Default create policy is upsert (create if not found); use dict form with create='lookup' for link-only. Prefer over deprecated top-level link_to.
          - string
          - string[]
          - object
        - `node_constraints` NodeConstraintInput[], nullable — Full node constraint objects. Same rules as policy.graph.link_to after expansion; use link_to for compact DSL or this field for explicit control. Both may be set.
          - `node_type` string, nullable — Node type this constraint applies to (e.g., 'Task', 'Project', 'Person'). Optional at schema level (implicit from parent UserNodeType), required at memory level (in memory_policy.node_constraints).
          - `when` object, nullable — Condition for when this constraint applies. Supports logical operators: '_and', '_or', '_not'. Examples: Simple: {'priority': 'high'} - matches when priority equals 'high'. AND: {'_and': [{'priority': 'high'}, {'status': 'active'}]} - all must match. OR: {'_or': [{'status': 'active'}, {'status': 'pending'}]} - any must match. NOT: {'_not': {'status': 'completed'}} - negation. Complex: {'_and': [{'priority': 'high'}, {'_or': [{'status': 'active'}, {'urgent': true}]}]}
          - `create` 'upsert' | 'lookup' | 'auto' | 'never' — 'upsert': Create if not found via search (default). 'lookup': Only link to existing nodes (controlled vocabulary). Deprecated aliases: 'auto' -> 'upsert', 'never' -> 'lookup'.
          - `on_miss` 'create' | 'ignore' | 'error', nullable — Explicit behavior when no match found via search. 'create': create new node (same as upsert). 'ignore': skip node creation (same as lookup). 'error': raise error if node not found. If specified, overrides 'create' field.
          - `link_only` boolean — DEPRECATED: Use create='lookup' instead. Shorthand for create='lookup'. When True, only links to existing nodes (controlled vocabulary). Equivalent to @lookup decorator in schema definitions.
          - `search` SearchConfigInput — Configuration for finding/selecting existing nodes. Defines which properties to match on and how, in priority order. The first matching property wins. **String Shorthand** (simple cases - converts to exact match): SearchConfig(properties=["id", "email"]) # Equivalent to: SearchConfig(properties=[PropertyMatch.exact("id"), PropertyMatch.exact("email")]) **Mixed Form** (combine strings and PropertyMatch): SearchConfig(properties=[ "id", # String -> exact match PropertyMatch.semantic("title", 0.9) # Full control ]) **Full Form** (maximum control): SearchConfig(properties=[ PropertyMatch(name="id", mode="exact"), PropertyMatch(name="title", mode="semantic", threshold=0.85) ]) **To select a specific node by ID**: SearchConfig(properties=[PropertyMatch.exact("id", "TASK-123")])
            - `properties` PropertyMatch[], nullable — Properties to match on, in priority order (first match wins). Accepts strings (converted to exact match) or PropertyMatch objects. Use PropertyMatch with 'value' field for specific node selection.
              - …
            - `via_relationship` RelationshipMatchInput[], nullable — Search for nodes via their relationships. Example: Find tasks assigned to a specific person. Each RelationshipMatch specifies edge_type, target_type, and target_search. Multiple relationship matches are ANDed together.
              - …
            - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
            - `threshold` number — Default similarity threshold for semantic/fuzzy matching (0.0-1.0). Used when property doesn't specify its own threshold.
          - `set` object, nullable — Set property values on nodes. Supports: 1. Exact value: {'status': 'done'} - sets exact value. 2. Auto-extract: {'status': {'mode': 'auto'}} - LLM extracts from content. 3. Text mode: {'summary': {'mode': 'auto', 'text_mode': 'merge'}} - controls text updates. For text properties, text_mode can be 'replace', 'append', or 'merge'.
        - `edge_constraints` EdgeConstraintInput[], nullable — Full edge constraint objects. Same rules as edge entries in policy.graph.link_to after expansion; both may be set in the same request.
          - `edge_type` string, nullable — Edge/relationship type this constraint applies to (e.g., 'MITIGATES', 'ASSIGNED_TO'). Optional at schema level (implicit from parent UserRelationshipType), required at memory level (in memory_policy.edge_constraints).
          - `source_type` string, nullable — Filter: only apply when source node is of this type. Example: source_type='SecurityBehavior' - only applies to edges from SecurityBehavior nodes.
          - `target_type` string, nullable — Filter: only apply when target node is of this type. Example: target_type='TacticDef' - only applies to edges targeting TacticDef nodes.
          - `direction` 'outgoing' | 'incoming' | 'both' — Direction of edges this constraint applies to. 'outgoing': edges where current node is source (default). 'incoming': edges where current node is target. 'both': applies in either direction.
          - `when` object, nullable — Condition for when this constraint applies. Supports logical operators: '_and', '_or', '_not'. Applied to edge properties or context. Example: {'_and': [{'severity': 'high'}, {'_not': {'status': 'deprecated'}}]}
          - `create` 'upsert' | 'lookup' | 'auto' | 'never' — 'upsert': Create target node if not found via search (default). 'lookup': Only link to existing target nodes (controlled vocabulary). When 'lookup', edges to non-existing targets are skipped. Deprecated aliases: 'auto' -> 'upsert', 'never' -> 'lookup'.
          - `on_miss` 'create' | 'ignore' | 'error', nullable — Explicit behavior when no target match found via search. 'create': create new target node (same as upsert). 'ignore': skip edge creation (same as lookup). 'error': raise error if target not found. If specified, overrides 'create' field.
          - `link_only` boolean — DEPRECATED: Use create='lookup' instead. Shorthand for create='lookup'. When True, only links to existing target nodes. Equivalent to @lookup decorator in schema definitions.
          - `search` SearchConfigInput — Configuration for finding/selecting existing nodes. Defines which properties to match on and how, in priority order. The first matching property wins. **String Shorthand** (simple cases - converts to exact match): SearchConfig(properties=["id", "email"]) # Equivalent to: SearchConfig(properties=[PropertyMatch.exact("id"), PropertyMatch.exact("email")]) **Mixed Form** (combine strings and PropertyMatch): SearchConfig(properties=[ "id", # String -> exact match PropertyMatch.semantic("title", 0.9) # Full control ]) **Full Form** (maximum control): SearchConfig(properties=[ PropertyMatch(name="id", mode="exact"), PropertyMatch(name="title", mode="semantic", threshold=0.85) ]) **To select a specific node by ID**: SearchConfig(properties=[PropertyMatch.exact("id", "TASK-123")])
            - `properties` PropertyMatch[], nullable — Properties to match on, in priority order (first match wins). Accepts strings (converted to exact match) or PropertyMatch objects. Use PropertyMatch with 'value' field for specific node selection.
              - …
            - `via_relationship` RelationshipMatchInput[], nullable — Search for nodes via their relationships. Example: Find tasks assigned to a specific person. Each RelationshipMatch specifies edge_type, target_type, and target_search. Multiple relationship matches are ANDed together.
              - …
            - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
            - `threshold` number — Default similarity threshold for semantic/fuzzy matching (0.0-1.0). Used when property doesn't specify its own threshold.
          - `set` object, nullable — Set property values on edges. Supports: 1. Exact value: {'weight': 1.0} - sets exact value. 2. Auto-extract: {'reason': {'mode': 'auto'}} - LLM extracts from content. Edge properties are useful for relationship metadata (weight, timestamp, reason, etc.).
        - `nodes` NodeSpec[], nullable
          - `id` string, required — Unique identifier for this node
          - `type` string, required — Node type/label (e.g., 'Transaction', 'Product', 'Person')
          - `properties` object — Properties for this node
        - `relationships` RelationshipSpec[], nullable
          - `source` string, required — ID of the source node
          - `target` string, required — ID of the target node
          - `type` string, required — Relationship type (e.g., 'PURCHASED', 'WORKS_AT', 'ASSIGNED_TO')
          - `properties` object, nullable — Optional properties for this relationship
    - `memory_policy` object, nullable — Unified memory processing policy. This is the SINGLE source of truth for how a memory should be processed, combining graph generation control AND OMO (Open Memory Object) safety standards. **Graph Generation Modes:** - auto: LLM extracts entities freely (default) - manual: Developer provides exact nodes (no LLM extraction) **OMO Safety Standards:** - consent: How data owner allowed storage (explicit, implicit, terms, none) - risk: Safety assessment (none, sensitive, flagged) - acl: Access control list for read/write permissions **Schema Integration:** - schema_id: Reference a schema that may have its own default memory_policy - Schema-level policies are merged with request-level (request takes precedence)
      - `mode` 'auto' | 'manual' — Memory processing mode - describes WHO controls graph generation. - AUTO: LLM extracts entities freely (default) - MANUAL: Developer provides exact nodes (no LLM extraction) Note: 'structured' is accepted as a deprecated alias for 'manual'.
      - `nodes` NodeSpec[], nullable — For manual mode: Exact nodes to create (no LLM extraction). Required when mode='manual'. Each node needs id, type, and properties.
        - `id` string, required — Unique identifier for this node
        - `type` string, required — Node type/label (e.g., 'Transaction', 'Product', 'Person')
        - `properties` object — Properties for this node
      - `relationships` RelationshipSpec[], nullable — Relationships between nodes. Supports special placeholders: '$this' = the Memory node being created, '$previous' = the user's most recent memory. Examples: {source: '$this', target: '$previous', type: 'FOLLOWS'} links to previous memory. {source: '$this', target: 'mem_abc', type: 'REFERENCES'} links to specific memory.
        - `source` string, required — ID of the source node
        - `target` string, required — ID of the target node
        - `type` string, required — Relationship type (e.g., 'PURCHASED', 'WORKS_AT', 'ASSIGNED_TO')
        - `properties` object, nullable — Optional properties for this relationship
      - `node_constraints` NodeConstraintInput[], nullable — Rules for how LLM-extracted nodes should be created/updated. Used in 'auto' mode when present. Controls creation policy, property forcing, and merge behavior.
        - `node_type` string, nullable — Node type this constraint applies to (e.g., 'Task', 'Project', 'Person'). Optional at schema level (implicit from parent UserNodeType), required at memory level (in memory_policy.node_constraints).
        - `when` object, nullable — Condition for when this constraint applies. Supports logical operators: '_and', '_or', '_not'. Examples: Simple: {'priority': 'high'} - matches when priority equals 'high'. AND: {'_and': [{'priority': 'high'}, {'status': 'active'}]} - all must match. OR: {'_or': [{'status': 'active'}, {'status': 'pending'}]} - any must match. NOT: {'_not': {'status': 'completed'}} - negation. Complex: {'_and': [{'priority': 'high'}, {'_or': [{'status': 'active'}, {'urgent': true}]}]}
        - `create` 'upsert' | 'lookup' | 'auto' | 'never' — 'upsert': Create if not found via search (default). 'lookup': Only link to existing nodes (controlled vocabulary). Deprecated aliases: 'auto' -> 'upsert', 'never' -> 'lookup'.
        - `on_miss` 'create' | 'ignore' | 'error', nullable — Explicit behavior when no match found via search. 'create': create new node (same as upsert). 'ignore': skip node creation (same as lookup). 'error': raise error if node not found. If specified, overrides 'create' field.
        - `link_only` boolean — DEPRECATED: Use create='lookup' instead. Shorthand for create='lookup'. When True, only links to existing nodes (controlled vocabulary). Equivalent to @lookup decorator in schema definitions.
        - `search` SearchConfigInput — Configuration for finding/selecting existing nodes. Defines which properties to match on and how, in priority order. The first matching property wins. **String Shorthand** (simple cases - converts to exact match): SearchConfig(properties=["id", "email"]) # Equivalent to: SearchConfig(properties=[PropertyMatch.exact("id"), PropertyMatch.exact("email")]) **Mixed Form** (combine strings and PropertyMatch): SearchConfig(properties=[ "id", # String -> exact match PropertyMatch.semantic("title", 0.9) # Full control ]) **Full Form** (maximum control): SearchConfig(properties=[ PropertyMatch(name="id", mode="exact"), PropertyMatch(name="title", mode="semantic", threshold=0.85) ]) **To select a specific node by ID**: SearchConfig(properties=[PropertyMatch.exact("id", "TASK-123")])
          - `properties` PropertyMatch[], nullable — Properties to match on, in priority order (first match wins). Accepts strings (converted to exact match) or PropertyMatch objects. Use PropertyMatch with 'value' field for specific node selection.
            - `name` string, required — Property name to match on (e.g., 'id', 'email', 'title')
            - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
            - `threshold` number — Similarity threshold for semantic/fuzzy modes (0.0-1.0). Ignored for exact mode.
            - `value` unknown
          - `via_relationship` RelationshipMatchInput[], nullable — Search for nodes via their relationships. Example: Find tasks assigned to a specific person. Each RelationshipMatch specifies edge_type, target_type, and target_search. Multiple relationship matches are ANDed together.
            - `edge_type` string, required — The relationship type to traverse (e.g., 'ASSIGNED_TO', 'BELONGS_TO')
            - `target_type` string, required — The target node type at the end of the relationship (e.g., 'Person', 'Project')
            - `target_search` SearchConfigInput, required — recursive
            - `direction` 'outgoing' | 'incoming' — Direction of the relationship from the node being searched. 'outgoing': node --edge--> target (default). 'incoming': target --edge--> node.
          - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
          - `threshold` number — Default similarity threshold for semantic/fuzzy matching (0.0-1.0). Used when property doesn't specify its own threshold.
        - `set` object, nullable — Set property values on nodes. Supports: 1. Exact value: {'status': 'done'} - sets exact value. 2. Auto-extract: {'status': {'mode': 'auto'}} - LLM extracts from content. 3. Text mode: {'summary': {'mode': 'auto', 'text_mode': 'merge'}} - controls text updates. For text properties, text_mode can be 'replace', 'append', or 'merge'.
      - `edge_constraints` EdgeConstraintInput[], nullable — Rules for how LLM-extracted edges/relationships should be created/handled. Used in 'auto' mode when present. Controls: - create: 'auto' (create target if not found) or 'never' (controlled vocabulary) - search: How to find existing target nodes - set: Edge property values (exact or auto-extracted) - source_type/target_type: Filter by connected node types Example: {edge_type: 'MITIGATES', create: 'never', search: {properties: ['name']}}
        - `edge_type` string, nullable — Edge/relationship type this constraint applies to (e.g., 'MITIGATES', 'ASSIGNED_TO'). Optional at schema level (implicit from parent UserRelationshipType), required at memory level (in memory_policy.edge_constraints).
        - `source_type` string, nullable — Filter: only apply when source node is of this type. Example: source_type='SecurityBehavior' - only applies to edges from SecurityBehavior nodes.
        - `target_type` string, nullable — Filter: only apply when target node is of this type. Example: target_type='TacticDef' - only applies to edges targeting TacticDef nodes.
        - `direction` 'outgoing' | 'incoming' | 'both' — Direction of edges this constraint applies to. 'outgoing': edges where current node is source (default). 'incoming': edges where current node is target. 'both': applies in either direction.
        - `when` object, nullable — Condition for when this constraint applies. Supports logical operators: '_and', '_or', '_not'. Applied to edge properties or context. Example: {'_and': [{'severity': 'high'}, {'_not': {'status': 'deprecated'}}]}
        - `create` 'upsert' | 'lookup' | 'auto' | 'never' — 'upsert': Create target node if not found via search (default). 'lookup': Only link to existing target nodes (controlled vocabulary). When 'lookup', edges to non-existing targets are skipped. Deprecated aliases: 'auto' -> 'upsert', 'never' -> 'lookup'.
        - `on_miss` 'create' | 'ignore' | 'error', nullable — Explicit behavior when no target match found via search. 'create': create new target node (same as upsert). 'ignore': skip edge creation (same as lookup). 'error': raise error if target not found. If specified, overrides 'create' field.
        - `link_only` boolean — DEPRECATED: Use create='lookup' instead. Shorthand for create='lookup'. When True, only links to existing target nodes. Equivalent to @lookup decorator in schema definitions.
        - `search` SearchConfigInput — Configuration for finding/selecting existing nodes. Defines which properties to match on and how, in priority order. The first matching property wins. **String Shorthand** (simple cases - converts to exact match): SearchConfig(properties=["id", "email"]) # Equivalent to: SearchConfig(properties=[PropertyMatch.exact("id"), PropertyMatch.exact("email")]) **Mixed Form** (combine strings and PropertyMatch): SearchConfig(properties=[ "id", # String -> exact match PropertyMatch.semantic("title", 0.9) # Full control ]) **Full Form** (maximum control): SearchConfig(properties=[ PropertyMatch(name="id", mode="exact"), PropertyMatch(name="title", mode="semantic", threshold=0.85) ]) **To select a specific node by ID**: SearchConfig(properties=[PropertyMatch.exact("id", "TASK-123")])
          - `properties` PropertyMatch[], nullable — Properties to match on, in priority order (first match wins). Accepts strings (converted to exact match) or PropertyMatch objects. Use PropertyMatch with 'value' field for specific node selection.
            - `name` string, required — Property name to match on (e.g., 'id', 'email', 'title')
            - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
            - `threshold` number — Similarity threshold for semantic/fuzzy modes (0.0-1.0). Ignored for exact mode.
            - `value` unknown
          - `via_relationship` RelationshipMatchInput[], nullable — Search for nodes via their relationships. Example: Find tasks assigned to a specific person. Each RelationshipMatch specifies edge_type, target_type, and target_search. Multiple relationship matches are ANDed together.
            - `edge_type` string, required — The relationship type to traverse (e.g., 'ASSIGNED_TO', 'BELONGS_TO')
            - `target_type` string, required — The target node type at the end of the relationship (e.g., 'Person', 'Project')
            - `target_search` SearchConfigInput, required — recursive
            - `direction` 'outgoing' | 'incoming' — Direction of the relationship from the node being searched. 'outgoing': node --edge--> target (default). 'incoming': target --edge--> node.
          - `mode` 'semantic' | 'exact' | 'fuzzy' — Search mode for finding existing nodes.
          - `threshold` number — Default similarity threshold for semantic/fuzzy matching (0.0-1.0). Used when property doesn't specify its own threshold.
        - `set` object, nullable — Set property values on edges. Supports: 1. Exact value: {'weight': 1.0} - sets exact value. 2. Auto-extract: {'reason': {'mode': 'auto'}} - LLM extracts from content. Edge properties are useful for relationship metadata (weight, timestamp, reason, etc.).
      - `schema_id` string, nullable — Reference a UserGraphSchema by ID. The schema's memory_policy (if defined) will be used as defaults, with this request's settings taking precedence.
      - `consent` 'explicit' | 'implicit' | 'terms' | 'none' — How the data owner allowed this memory to be stored/used. Aligned with Open Memory Object (OMO) standard.
      - `risk` 'none' | 'sensitive' | 'flagged' — Post-ingest safety assessment of memory content. Aligned with Open Memory Object (OMO) standard.
      - `acl` ACLConfig — Simplified Access Control List configuration. Aligned with Open Memory Object (OMO) standard. See: https://github.com/anthropics/open-memory-object **Supported Entity Prefixes:** | Prefix | Description | Validation | |--------|-------------|------------| | `user:` | Internal Papr user ID | Validated against Parse users | | `external_user:` | Your app's user ID | Not validated (your responsibility) | | `organization:` | Organization ID | Validated against your organizations | | `namespace:` | Namespace ID | Validated against your namespaces | | `workspace:` | Workspace ID | Validated against your workspaces | | `role:` | Parse role ID | Validated against your roles | **Examples:** ```python acl = ACLConfig( read=["external_user:alice_123", "organization:org_acme"], write=["external_user:alice_123"] ) ``` **Validation Rules:** - Internal entities (user, organization, namespace, workspace, role) are validated - External entities (external_user) are NOT validated - your app is responsible - Invalid internal entities will return an error - Unprefixed values default to `external_user:` for backwards compatibility
        - `read` string[] — Entity IDs that can read this memory. Format: 'prefix:id' (e.g., 'external_user:alice', 'organization:org_123'). Supported prefixes: user, external_user, organization, namespace, workspace, role. Unprefixed values treated as external_user for backwards compatibility.
        - `write` string[] — Entity IDs that can write/modify this memory. Format: 'prefix:id' (e.g., 'external_user:alice'). Supported prefixes: user, external_user, organization, namespace, workspace, role.
    - `link_to` union — DEPRECATED: Use policy.graph.link_to instead. Shorthand DSL for node/edge constraints (same as node_constraints, compact syntax). Expands and merges into memory_policy.node_constraints and edge_constraints at resolve time. Default create is upsert; use dict form with create='lookup' (or legacy 'never') for link-only. Formats: - String: 'Task:title' (semantic match on Task.title, upsert by default) - List: ['Task:title', 'Person:email'] (multiple constraints) - Dict: {'Task:title': {'set': {...}, 'create': 'lookup'}} (full options) Syntax: - Node: 'Type:property', 'Type:prop=value' (exact), 'Type:prop~value' (semantic) - Edge: 'Source->EDGE->Target:property' (arrow syntax) - Via: 'Type.via(EDGE->Target:prop)' (relationship traversal) - Special: '$this', '$previous', '$context:N' Example lookup-only: {'SecurityPolicy:name': {'create': 'lookup'}}
      - string
      - string[]
      - object
    - `graph_generation` object, nullable — Graph generation configuration
      - `mode` 'auto' | 'manual' — Graph generation modes
      - `auto` AutoGraphGeneration — AI-powered graph generation with optional guidance
        - `schema_id` string, nullable — Force AI to use this specific schema instead of auto-selecting
        - `property_overrides` PropertyOverrideRule[], nullable — Override specific property values in AI-generated nodes with match conditions
          - `nodeLabel` string, required — Node type to apply overrides to (e.g., 'User', 'SecurityBehavior')
          - `match` object, nullable — Optional conditions that must be met for override to apply. If not provided, applies to all nodes of this type
          - `set` object, required — Properties to set/override on matching nodes
      - `manual` ManualGraphGeneration — Complete manual control over graph structure
        - `nodes` GraphOverrideNode[], required — Exact nodes to create
          - `id` string, required — **REQUIRED**: Unique identifier for this node. Must be unique within this request. Relationships reference this via source_node_id/target_node_id. Example: 'person_john_123', 'finding_cve_2024_1234'
          - `label` string, required — **REQUIRED**: Node type from your UserGraphSchema. View available types at GET /v1/schemas. System types: Memory, Person, Company, Project, Task, Insight, Meeting, Opportunity, Code
          - `properties` object, required — **REQUIRED**: Node properties matching your UserGraphSchema definition. Must include: (1) All required properties from your schema, (2) unique_identifiers if defined (e.g., 'email' for Person) to enable MERGE deduplication. View schema requirements at GET /v1/schemas
        - `relationships` GraphOverrideRelationship[] — Exact relationships to create
          - `source_node_id` string, required — **REQUIRED**: Must exactly match the 'id' field of a node defined in the 'nodes' array of this request
          - `target_node_id` string, required — **REQUIRED**: Must exactly match the 'id' field of a node defined in the 'nodes' array of this request
          - `relationship_type` string, required — **REQUIRED**: Relationship type from your UserGraphSchema. View available types at GET /v1/schemas. System types: WORKS_FOR, WORKS_ON, HAS_PARTICIPANT, DISCUSSES, MENTIONS, RELATES_TO, CREATED_BY
          - `properties` object, nullable — Optional relationship properties (e.g., {'since': '2024-01-01', 'role': 'manager'})
    - `content` string, required — The content of the memory item you want to add to memory
    - `type` 'text' | 'code_snippet' | 'document' — Valid memory types
    - `organization_id` string, nullable — DEPRECATED - Internal only. Auto-populated from API key scope. Do not set manually. The organization is resolved automatically from the API key's associated organization.
    - `namespace_id` string, nullable — Optional namespace ID for multi-tenant memory scoping. When provided, memory is associated with this namespace.
    - `external_user_id` string, nullable — Your application's user identifier. This is the primary way to identify users. Use this for your app's user IDs (e.g., 'user_alice_123', UUID, email). Papr will automatically resolve or create internal users as needed.
    - `user_id` string, nullable — DEPRECATED: Use 'external_user_id' instead. Internal Papr Parse user ID. Most developers should not use this field directly.
    - `metadata` MemoryMetadata — Metadata for memory request
      - `hierarchical_structures` union — Hierarchical structures to enable navigation from broad topics to specific ones
        - string
        - unknown[]
          - unknown
      - `createdAt` string, nullable — ISO datetime when the memory was created
      - `location` string, nullable
      - `topics` string[], nullable
      - `emoji tags` string[], nullable
      - `emotion tags` string[], nullable
      - `conversationId` string, nullable
      - `sourceUrl` string, nullable
      - `role` 'user' | 'assistant' — Role of the message sender
      - `category` 'preference' | 'task' | 'goal' | 'fact' | 'context' | 'skills' | 'learning', nullable — Memory category based on role. For users: preference, task, goal, fact, context. For assistants: skills, learning, task, goal, fact, context.
      - `user_id` string, nullable — DEPRECATED: Use 'external_user_id' at request level instead. This field will be removed in v2.
      - `external_user_id` string, nullable — DEPRECATED: Use 'external_user_id' at request level instead. This field will be removed in v2.
      - `external_user_read_access` string[], nullable — INTERNAL: Auto-populated for vector store filtering. Use memory_policy.acl instead.
      - `external_user_write_access` string[], nullable — INTERNAL: Auto-populated for vector store filtering. Use memory_policy.acl instead.
      - `user_read_access` string[], nullable — INTERNAL: Auto-populated for vector store filtering. Use memory_policy.acl instead.
      - `user_write_access` string[], nullable — INTERNAL: Auto-populated for vector store filtering. Use memory_policy.acl instead.
      - `workspace_read_access` string[], nullable — INTERNAL: Auto-populated for vector store filtering. Use memory_policy.acl instead.
      - `workspace_write_access` string[], nullable — INTERNAL: Auto-populated for vector store filtering. Use memory_policy.acl instead.
      - `role_read_access` string[], nullable — INTERNAL: Auto-populated for vector store filtering. Use memory_policy.acl instead.
      - `role_write_access` string[], nullable — INTERNAL: Auto-populated for vector store filtering. Use memory_policy.acl instead.
      - `namespace_read_access` string[], nullable — INTERNAL: Auto-populated for vector store filtering. Use memory_policy.acl instead.
      - `namespace_write_access` string[], nullable — INTERNAL: Auto-populated for vector store filtering. Use memory_policy.acl instead.
      - `organization_read_access` string[], nullable — INTERNAL: Auto-populated for vector store filtering. Use memory_policy.acl instead.
      - `organization_write_access` string[], nullable — INTERNAL: Auto-populated for vector store filtering. Use memory_policy.acl instead.
      - `pageId` string, nullable
      - `sourceType` string, nullable
      - `workspace_id` string, nullable
      - `upload_id` string, nullable — Upload ID for document processing workflows
      - `organization_id` string, nullable — DEPRECATED: Use 'organization_id' at request level instead. This field will be removed in v2.
      - `namespace_id` string, nullable — DEPRECATED: Use 'namespace_id' at request level instead. This field will be removed in v2.
      - `consent` string, nullable — DEPRECATED: Use 'memory_policy.consent' at request level instead. Values: 'explicit', 'implicit' (default), 'terms', 'none'.
      - `risk` string, nullable — DEPRECATED: Use 'memory_policy.risk' at request level instead. Values: 'none' (default), 'sensitive', 'flagged'.
      - `acl` object, nullable — DEPRECATED: Use 'memory_policy.acl' at request level instead. Format: {'read': [...], 'write': [...]}.
      - `sessionId` string, nullable
      - `post` string, nullable
      - `userMessage` string, nullable
      - `assistantMessage` string, nullable
      - `relatedGoals` string[], nullable
      - `relatedUseCases` string[], nullable
      - `relatedSteps` string[], nullable
      - `goalClassificationScores` number[], nullable
      - `useCaseClassificationScores` number[], nullable
      - `stepClassificationScores` number[], nullable
      - `customMetadata` object, nullable — Optional object for arbitrary custom metadata fields. Only string, number, boolean, or list of strings allowed. Nested dicts are not allowed.
    - `context` ContextItem[], nullable — Conversation history context for this memory. Use for providing message history when adding a memory. Format: [{role: 'user'|'assistant', content: '...'}]
      - `role` 'user' | 'assistant', required
      - `content` string, required
    - `relationships_json` RelationshipItem[], nullable — DEPRECATED: Use 'memory_policy' instead. Migration options: 1. Specific memory: relationships=[{source: '$this', target: 'mem_123', type: 'FOLLOWS'}] 2. Previous memory: link_to_previous_memory=True 3. Related memories: link_to_related_memories=3
      - `relation_type` string, required
      - `related_item_id` string, nullable
      - `relationship_type` 'previous_memory_item_id' | 'all_previous_memory_items' | 'link_to_id' — Enum for relationship types
      - `related_item_type` string, nullable — Legacy field - not used in processing
      - `metadata` object
  - `batch_size` integer, nullable — Number of items to process in parallel
  - `webhook_url` string, nullable — Optional webhook URL to notify when batch processing is complete. The webhook will receive a POST request with batch completion details.
  - `webhook_secret` string, nullable — Optional secret key for webhook authentication. If provided, will be included in the webhook request headers as 'X-Webhook-Secret'.

## Response `200`

Memories successfully added

- BatchMemoryResponse
  - `code` integer — HTTP status code for the batch operation
  - `status` string — 'success', 'partial', or 'error'
  - `message` string, nullable — Human-readable status message
  - `error` string, nullable — Batch-level error message, if any
  - `details` unknown
  - `batch_id` string, nullable — Batch tracking ID for status polling via GET /v1/memory/batch/status/{batch_id} and WebSocket updates
  - `successful` AddMemoryResponse[] — List of successful add responses
    - `code` integer — HTTP status code
    - `status` string — 'success' or 'error'
    - `data` AddMemoryItem[], nullable — List of memory items if successful
      - `memoryId` string, required
      - `createdAt` string, date-time, required
      - `objectId` string, required
      - `memoryChunkIds` string[]
    - `error` string, nullable — Error message if failed
    - `details` unknown
  - `errors` BatchMemoryError[] — List of errors for failed items
    - `index` integer, required
    - `error` string, required
    - `code` integer, nullable
    - `status` string, nullable
    - `details` unknown
  - `total_processed` integer
  - `total_successful` integer
  - `total_failed` integer
  - `total_content_size` integer
  - `total_storage_size` integer

## Other responses

- `207` — Partial success - some memories failed
- `400` — Bad request
- `401` — Unauthorized
- `403` — Subscription limit reached
- `413` — Content too large
- `415` — Unsupported Media Type
- `422` — Validation Error
- `500` — Internal server error

---

[API](https://skmtc.net/papr-ai/apis/papr-memory-api.md) · [All operations](https://skmtc.net/papr-ai/apis/papr-memory-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/papr-ai/papr-memory-api/versions/b553be9aad37/schema)
