{
  "openapi": "3.1.0",
  "info": {
    "title": "ChainMemory API",
    "version": "3.0",
    "summary": "Persistent, verifiable memory for AI agents, portable across models.",
    "description": "Memories are the input; **Project State** is the product: a consolidated, versioned view of a project distilled from those memories, with every item traceable to the memories that support it.\n\n**Authentication.** Every endpoint takes an API key in the `x-api-key` header, except those marked as public. Get one at https://faucet.chainmemory.ai\n\n**Fees.** Writing costs AIC, the network's usage credit: 0.001 per memory, 0.001 to assume a role, 0.1 per context injection, 0.05 + 0.005 per op for a state consolidation. Reading and verifying are free.\n\n**Scope of this document.** It covers the endpoints whose request and response contracts have been read in the server source or exercised against production. The API exposes more endpoints than these; the ones still undocumented are listed at the end of this description. They are left out on purpose: a generated client built on guessed schemas compiles and then fails at runtime, which is worse than not describing them at all.\n\nEndpoints that exist but are not yet described here:\n- `/v1/memory/fast`\n- `/v1/memory/attach`\n- `/v1/memory/context`\n- `/v1/memory/context/expanded`\n- `/v1/memory/{id}/forensics`\n- `/v1/memories`\n- `/v1/memories/{id}/trust`\n- `/v1/compress`\n- `/v1/inject/quote`\n- `/v1/audit/memory/{id}`\n- `/v1/audit/state/{project}`\n- `/v1/oracle/query`\n- `/v1/keys`\n- `/v1/keys/register-pubkey`\n- `/v1/seal/{id}`\n- `/v1/metrics`\n- `/v1/billing/status`\n- `/v1/projects/from-default/{id}`\n- `/v1/project/{name}/state/rollback`",
    "contact": {
      "name": "ChainMemory",
      "url": "https://chainmemory.ai"
    },
    "license": {
      "name": "MIT"
    }
  },
  "servers": [
    {
      "url": "https://api.chainmemory.ai",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Full documentation",
    "url": "https://docs.chainmemory.ai"
  },
  "tags": [
    {
      "name": "Memories",
      "description": "Write, search and manage memories"
    },
    {
      "name": "Blind vault",
      "description": "Memories encrypted client-side that the server cannot read"
    },
    {
      "name": "Project Brain",
      "description": "Consolidated, verifiable project state"
    },
    {
      "name": "Role contracts",
      "description": "Signed role contracts and audited sessions"
    },
    {
      "name": "Organizations",
      "description": "Teams: shared budget, member keys and audit trail"
    },
    {
      "name": "Verification",
      "description": "Public proofs: anyone can check an anchor without an API key"
    },
    {
      "name": "Context injection",
      "description": "Load verifiable memories into a model's context"
    },
    {
      "name": "Projects",
      "description": "Project tags"
    },
    {
      "name": "Account",
      "description": "Your identity and counters"
    },
    {
      "name": "Network",
      "description": "Public chain statistics"
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Get one free at https://faucet.chainmemory.ai"
      }
    }
  },
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/v1/memory": {
      "post": {
        "tags": [
          "Memories"
        ],
        "summary": "Write a memory",
        "description": "Writes a plaintext memory. The content is encrypted server-side with a key derived from your API key. Fee: 0.001 AIC. Maximum 19936 bytes of UTF-8 text (a single on-chain transaction); larger writes are rejected with 413 and no charge. The `event_hash` is computed when the memory is synced to the chain (~30 s later), not at write time — see the note on evidence in Project Brain.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "summary": {
                    "type": "string",
                    "description": "The memory text"
                  },
                  "category": {
                    "type": "string",
                    "description": "Free-form, e.g. DECISION, INTERACTION, CUSTOM"
                  },
                  "importance": {
                    "type": "integer",
                    "description": "1-10, default 5"
                  },
                  "platform": {
                    "type": "string",
                    "description": "claude, chatgpt, gemini..."
                  },
                  "project": {
                    "type": "string",
                    "description": "Project slug; stored as the first tag"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Up to 10 tags"
                  }
                },
                "required": [
                  "summary"
                ]
              },
              "example": {
                "summary": "Decided to migrate the primary host to Hetzner.",
                "category": "DECISION",
                "importance": 8,
                "project": "chainmemory",
                "tags": [
                  "infra",
                  "migration"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Memory written",
            "content": {
              "application/json": {
                "example": {
                  "memory_number": 617,
                  "event_hash": null,
                  "chain_memory_id": null,
                  "trust": "trusted",
                  "tags": [
                    "chainmemory",
                    "infra"
                  ]
                }
              }
            }
          },
          "402": {
            "description": "insufficient_aic",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "error": "insufficient_aic",
                  "required_aic": "0.001"
                }
              }
            }
          },
          "413": {
            "description": "memory_too_large — the text would not fit in a single on-chain transaction, so it could never be anchored or cited as evidence. Checked before charging, so nothing is charged (since 2026-09-16)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "error": "memory_too_large",
                  "bytes": 25000,
                  "max_bytes": 19936,
                  "over_by": 5064
                }
              }
            }
          }
        }
      }
    },
    "/v1/memory/sealed": {
      "post": {
        "tags": [
          "Blind vault"
        ],
        "summary": "Store an already-encrypted memory",
        "description": "Blind vault: the client encrypts the content and sends an opaque blob. The server charges the same fee, anchors the hash and stores bytes it cannot read. Unlike a plaintext memory, the `event_hash` comes from the client, so a sealed memory is citable as evidence immediately. Requires a custodial key able to sign the anchor. Optional `embedding` (since 2026-09-21): the memory's search vector, computed on the client. With it the memory appears in semantic search without the server reading it; the response says so in `searchable`. Without it the memory is stored anyway, but it can only be found by project or tag.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "blob_b64": {
                    "type": "string",
                    "description": "Versioned envelope, base64: 0x02 || IV || ciphertext || tag"
                  },
                  "event_hash": {
                    "type": "string",
                    "description": "SHA-256 computed by the client, 32 bytes hex"
                  },
                  "plain_len": {
                    "type": "integer",
                    "description": "Plaintext length, for quota accounting"
                  },
                  "category": {
                    "type": "string"
                  },
                  "importance": {
                    "type": "integer"
                  },
                  "platform": {
                    "type": "string"
                  },
                  "project": {
                    "type": "string"
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "embedding": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    },
                    "description": "384 numbers, L2-normalized (norm 1, tolerance 0.001): the all-MiniLM-L6-v2 vector computed on the client, [CLS] + up to 254 tokens + [SEP], mean pooling"
                  }
                },
                "required": [
                  "blob_b64",
                  "event_hash",
                  "plain_len"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sealed memory stored",
            "content": {
              "application/json": {
                "example": {
                  "memory_id": 640,
                  "memory_number": 616,
                  "scheme": "sealed",
                  "tags": [
                    "chainmemory"
                  ],
                  "chain_sync": "pending",
                  "searchable": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid blob_b64 or event_hash, or `embedding invalido: …` when the vector is not 384 finite numbers with norm 1",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "insufficient_aic",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Sealed writes need a custodial key able to sign the anchor",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "413": {
            "description": "Blob too large for one transaction",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/memory/{id}/blob": {
      "get": {
        "tags": [
          "Blind vault"
        ],
        "summary": "Retrieve a sealed blob for client-side decryption",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "memory_number"
          }
        ],
        "responses": {
          "200": {
            "description": "The encrypted blob",
            "content": {
              "application/json": {
                "example": {
                  "memory_number": 616,
                  "scheme": "sealed",
                  "blob_b64": "AqYz...",
                  "plain_len": 123
                }
              }
            }
          },
          "404": {
            "description": "Memory not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/memories/list": {
      "get": {
        "tags": [
          "Memories"
        ],
        "summary": "List memories, newest first",
        "description": "Pages of at most 100. The project filter is applied before paging (since 2026-09-15), so every page holds only memories of that project; follow `next_offset` until it is null. `total` and `total_archived` count the whole account, `total_filtered` counts what matches the filter. Tag matching is exact and case-sensitive.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "1-100, default 50. Larger values are capped at 100"
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Default 0. Use next_offset from the previous page"
          },
          {
            "name": "project",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Exact project tag. `general` returns memories with no tags"
          },
          {
            "name": "archived",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "1 returns ONLY archived memories; omit for active ones"
          },
          {
            "name": "synced",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "1 returns only memories already anchored on-chain"
          },
          {
            "name": "include_plaintext",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "1 adds `summary` with the full text of non-sealed memories. Sealed memories never include it: the server does not have their text"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of memories",
            "content": {
              "application/json": {
                "example": {
                  "memories": [
                    {
                      "id": 757,
                      "memory_number": 727,
                      "summary_preview": "BRAIN v90 CONSOLIDADO Y GUARD POR PROYECTO VERIFICADO EN VIVO...",
                      "summary": "(full text, only with include_plaintext=1)",
                      "summary_length": 1180,
                      "estimated_tokens": 295,
                      "category": "VERIFICACION",
                      "importance": 8,
                      "platform": "claude",
                      "timestamp": 1789435000,
                      "synced_chain": true,
                      "chain_memory_id": 918,
                      "event_hash": "0x…",
                      "tags": [
                        "chainmemory",
                        "brain"
                      ],
                      "archived": false,
                      "inject_count": 0,
                      "trust": "trusted",
                      "scheme": "plain"
                    }
                  ],
                  "count": 50,
                  "total": 733,
                  "total_archived": 49,
                  "total_filtered": 503,
                  "next_offset": 50,
                  "filter": {
                    "project": "chainmemory",
                    "archived": false,
                    "onlySynced": false
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/memory/{id}/decrypted": {
      "get": {
        "tags": [
          "Memories"
        ],
        "summary": "Read one of your memories from the chain and check it against your record",
        "description": "Takes your `memory_number`. Reads the ciphertext stored on-chain, decrypts it with the key derived from your API key and checks two things: that the text matches the on-chain hash, and (since 2026-09-14) that the on-chain hash matches the `event_hash` of your record (`matches_record`). `integrity_verified` is true only when both hold. A sealed memory is returned as its encrypted blob, never as text: decrypt it on the client.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "memory_number"
          }
        ],
        "responses": {
          "200": {
            "description": "Decrypted memory, or the blob of a sealed one",
            "content": {
              "application/json": {
                "example": {
                  "memory_number": 687,
                  "ai_id": 1,
                  "category": "RISK",
                  "summary": "…",
                  "summary_length": 612,
                  "event_hash": "0x…",
                  "integrity_verified": true,
                  "matches_record": true,
                  "timestamp": 1789339882,
                  "importance": 8,
                  "is_sealed": false
                }
              }
            }
          },
          "403": {
            "description": "caller not registered on chain",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "memory not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "memory not yet anchored on-chain (wait ~30 s after writing)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/memories/search": {
      "get": {
        "tags": [
          "Memories"
        ],
        "summary": "Semantic search over your memories",
        "description": "The query text travels in the URL: the server embeds it, keeps it in its search log, and it appears in web server access logs. To keep the query off the server, use POST with a vector instead. Sealed memories appear only if their `embedding` was sent when they were stored; they come back without text and, in this text search, without their blob: use POST, or GET /v1/memory/{id}/blob, to decrypt them on the client. Score = 0.5 × semantic similarity + 0.3 × recency (one-week half-life) + 0.2 × importance/10.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Natural-language query"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "1-20, default 10"
          }
        ],
        "responses": {
          "200": {
            "description": "Matches with a relevance score",
            "content": {
              "application/json": {
                "example": {
                  "count": 2,
                  "query": "hetzner migration",
                  "memories": [
                    {
                      "id": 617,
                      "summary": "...",
                      "_score": 0.71
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Memories"
        ],
        "summary": "Semantic search by vector: the query text never reaches the server",
        "description": "Same ranking and the same memories as GET (both use one scoring function), but the client sends the query's vector in the body instead of its text. The server never sees the query and logs only that a vector search happened. Sealed matches come back with `scheme: sealed`, `summary: null` and their `blob_b64`, so the client decrypts them without another request. Since 2026-09-21.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "query_embedding": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    },
                    "description": "384 numbers, L2-normalized (norm 1, tolerance 0.001): the all-MiniLM-L6-v2 vector computed on the client, [CLS] + up to 254 tokens + [SEP], mean pooling"
                  },
                  "limit": {
                    "type": "integer",
                    "description": "1-20, default 10"
                  }
                },
                "required": [
                  "query_embedding"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Matches with a relevance score",
            "content": {
              "application/json": {
                "example": {
                  "count": 2,
                  "memories": [
                    {
                      "id": 939,
                      "summary": null,
                      "category": "INTERACTION",
                      "importance": 5,
                      "timestamp": 1790428956,
                      "platform": "perplexity",
                      "chain_memory_id": 1103,
                      "trust": "trusted",
                      "_score": 0.6653,
                      "scheme": "sealed",
                      "blob_b64": "AqYz...",
                      "event_hash": "0x…",
                      "plain_len": 3347
                    },
                    {
                      "id": 942,
                      "summary": "…",
                      "category": "MILESTONE",
                      "importance": 8,
                      "timestamp": 1790436000,
                      "platform": "claude",
                      "chain_memory_id": 1106,
                      "trust": "trusted",
                      "_score": 0.6369,
                      "scheme": "plain"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "`query_embedding invalido: …` — not an array of 384 finite numbers with norm 1",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/memories/{id}/archive": {
      "post": {
        "tags": [
          "Memories"
        ],
        "summary": "Archive a memory (reversible)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/memories/{id}/unarchive": {
      "post": {
        "tags": [
          "Memories"
        ],
        "summary": "Restore an archived memory",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Done"
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/memories/{id}/tags": {
      "put": {
        "tags": [
          "Memories"
        ],
        "summary": "Replace the tags of a memory",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                },
                "required": [
                  "tags"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tags updated"
          }
        }
      }
    },
    "/v1/profile": {
      "get": {
        "tags": [
          "Account"
        ],
        "summary": "Your account and memory counters",
        "description": "Counters for YOUR account only. `local_memories` matches the `total` of /v1/memories/list; `chain_memories` counts what the memory contract holds for your AI id. Network-wide numbers live in /v1/stats and are not comparable with these.",
        "responses": {
          "200": {
            "description": "Profile",
            "content": {
              "application/json": {
                "example": {
                  "ai_id": 1,
                  "owner": "0x46b4...",
                  "chain_memories": 720,
                  "local_memories": 616,
                  "synced_memories": 616,
                  "pending_sync": 0,
                  "reputation": 4831,
                  "active": true
                }
              }
            }
          }
        }
      }
    },
    "/v1/stats": {
      "get": {
        "tags": [
          "Network"
        ],
        "summary": "Public network statistics",
        "security": [],
        "description": "Network-wide, every account. `total_memories` counts write operations on the memory contract, so it runs ahead of `episodic_memories` (rows in the database): before the fix of 2026-09-14 an overlapping sync could write the same memory twice, and those 178 extra writes remain on-chain. For your own account use /v1/profile.",
        "responses": {
          "200": {
            "description": "Network stats",
            "content": {
              "application/json": {
                "example": {
                  "network": "ChainMemory",
                  "chain_id": 202604,
                  "block": 662072,
                  "total_ais": 13,
                  "total_memories": 752,
                  "episodic_memories": 646
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "List your projects",
        "responses": {
          "200": {
            "description": "Projects",
            "content": {
              "application/json": {
                "example": {
                  "count": 7,
                  "projects": [
                    {
                      "id": 1,
                      "project_id": "chainmemory",
                      "name": "chainmemory",
                      "keywords": [
                        "blockchain"
                      ],
                      "color": "#1119e8"
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Projects"
        ],
        "summary": "Create a project",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "project_id": {
                    "type": "string",
                    "description": "Short slug"
                  },
                  "name": {
                    "type": "string",
                    "description": "Display name"
                  },
                  "keywords": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Auto-tag keywords. MUST be an array, even if empty"
                  }
                },
                "required": [
                  "project_id",
                  "name"
                ]
              },
              "example": {
                "project_id": "my-project",
                "name": "My Project",
                "keywords": []
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created"
          },
          "400": {
            "description": "Validation error, e.g. 'keywords must be array'",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects/defaults": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Built-in project templates",
        "responses": {
          "200": {
            "description": "Templates",
            "content": {
              "application/json": {
                "example": {
                  "defaults": [
                    {
                      "project_id": "research",
                      "name": "Research",
                      "keywords": [
                        "paper"
                      ],
                      "description": "..."
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/projects/{id}": {
      "delete": {
        "tags": [
          "Projects"
        ],
        "summary": "Delete a project tag",
        "description": "Takes the NUMERIC id from /v1/projects, not the slug. Passing the slug returns 400 'invalid id'. Memories keep the tag.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Numeric project id"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted"
          },
          "400": {
            "description": "invalid id",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/project/{name}/state": {
      "get": {
        "tags": [
          "Project Brain"
        ],
        "summary": "Consolidated, verifiable project state",
        "description": "The distilled state of a project: phase, current focus, decisions with their evidence, risks, priorities, open questions, vocabulary, constraints and the `environment` section (hosts, services, repositories and operating rules). Includes `state_hash` (SHA3-256) for integrity.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "include_roles",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "Embed the full text of every signed role contract"
          }
        ],
        "responses": {
          "200": {
            "description": "Project state, its on-chain anchor and a `provenance` block (since 2026-09-16) counting how many items cite memories and how many carry `evidence_root` 0x000...0 — state with no provenance",
            "content": {
              "application/json": {
                "example": {
                  "project": "chainmemory",
                  "schema_version": 2,
                  "version": 91,
                  "state_hash": "0xf47ce268…",
                  "generated_at": "2026-09-15T21:34:33.000Z",
                  "anchor": {
                    "status": "anchored",
                    "onchain_anchor_id": 98,
                    "tx_hash": "0x…",
                    "block_number": 693319
                  },
                  "provenance": {
                    "items": 208,
                    "with_evidence": 140,
                    "without_evidence": 68,
                    "pct_with_evidence": 67.3,
                    "zero_root": "0x000…0"
                  },
                  "state": {
                    "phase": "…",
                    "decisions": [
                      "…"
                    ]
                  }
                }
              }
            }
          },
          "404": {
            "description": "No state yet",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/project/{name}/state/ops": {
      "post": {
        "tags": [
          "Project Brain"
        ],
        "summary": "Apply operations to the project state",
        "description": "Client consolidates, chain verifies. You send operations from the 29-op grammar; the server validates invariants, applies them with a deterministic builder, computes the new `state_hash` and persists it. Fee: 0.05 AIC + 0.005 per applied op.\n\n**Evidence is what makes the state trustworthy.** Cite the memories that support each operation with `evidence_memory_ids`; the server resolves them to event hashes and computes a Merkle `evidence_root`. An operation written without evidence gets `evidence_root 0x000...0` — state with no provenance, indistinguishable from the model's own opinion, and the chain will seal it just the same because it verifies hashes, not correctness.\n\n**If any cited memory cannot be resolved, the whole call is rejected with 422** and nothing is written or charged. The usual cause is citing a plaintext memory before it has been anchored: its `event_hash` is computed on sync (~30 s), not on write. Sealed memories carry their hash from the client and are citable immediately.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ops": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "op": {
                          "type": "string",
                          "description": "Operation type: add_decision, add_milestone, add_risk, set_focus, add_env_host, ..."
                        },
                        "evidence_memory_ids": {
                          "type": "array",
                          "items": {
                            "type": "integer"
                          },
                          "description": "Memory numbers backing this operation"
                        }
                      }
                    },
                    "description": "1-100 operations"
                  },
                  "generated_by": {
                    "type": "string",
                    "description": "Client identifier"
                  },
                  "consolidated_until_event": {
                    "type": "integer",
                    "description": "Highest memory number included in this consolidation"
                  }
                },
                "required": [
                  "ops"
                ]
              },
              "example": {
                "ops": [
                  {
                    "op": "add_milestone",
                    "status": "done",
                    "title": "Primary host migrated to Hetzner.",
                    "evidence_memory_ids": [
                      617,
                      618
                    ]
                  }
                ],
                "consolidated_until_event": 618
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "State updated",
            "content": {
              "application/json": {
                "example": {
                  "version": 85,
                  "state_hash": "0x09a8...",
                  "applied_count": 2,
                  "rejected": [],
                  "ops_log": [],
                  "provenance_warning": {
                    "ops_sin_evidencia": [
                      {
                        "index": 0,
                        "op": "add_milestone"
                      }
                    ],
                    "memorias_sin_consolidar": 12
                  }
                }
              }
            }
          },
          "200": {
            "description": "No change: every op was rejected, or the resulting hash is identical"
          },
          "422": {
            "description": "evidence_unresolved — cited memories could not be resolved; nothing written, nothing charged",
            "content": {
              "application/json": {
                "example": {
                  "error": "evidence_unresolved",
                  "sin_anclar": 12,
                  "no_existen": 0,
                  "ops": [
                    {
                      "index": 0,
                      "op": "add_milestone",
                      "citadas": 13,
                      "resueltas": 1,
                      "sin_anclar": [
                        618,
                        619
                      ]
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "description": "insufficient_aic",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/project/{name}/state/anchor": {
      "get": {
        "tags": [
          "Project Brain"
        ],
        "summary": "On-chain anchor of a state version",
        "security": [],
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Defaults to the latest anchored"
          }
        ],
        "responses": {
          "200": {
            "description": "Anchor"
          },
          "404": {
            "description": "No anchors yet",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/project/{name}/roles": {
      "get": {
        "tags": [
          "Role contracts"
        ],
        "summary": "Roles defined for a project",
        "description": "Role ids are not guessable: list them before assuming one. Only contracts with status `active` can be assumed.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Roles",
            "content": {
              "application/json": {
                "example": {
                  "project": "chainmemory",
                  "roles": [
                    {
                      "role_id": "charly",
                      "version": 2,
                      "status": "active",
                      "contract_hash": "0x1a55...",
                      "author": "silvio",
                      "signed_at": 1784810494
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/project/{name}/role/{roleId}": {
      "get": {
        "tags": [
          "Role contracts"
        ],
        "summary": "Read a Verifiable Role Contract in full",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "roleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Audit a past version"
          }
        ],
        "responses": {
          "200": {
            "description": "Contract: purpose, rules with severity, working protocol"
          },
          "404": {
            "description": "role contract not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/project/{name}/role/{roleId}/assume": {
      "post": {
        "tags": [
          "Role contracts"
        ],
        "summary": "Open an audited session under a role",
        "description": "Pins the contract version and hash plus the Brain state hash at that instant, and returns the owner's declared working environment. Fee: 0.001 AIC. Auto-releases after 60 minutes. One open session per role.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "roleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "platform": {
                    "type": "string",
                    "description": "Executor: claude, chatgpt, gemini"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Session open",
            "content": {
              "application/json": {
                "example": {
                  "session_id": 24,
                  "contract_version": 2,
                  "contract_hash": "0x1a55...",
                  "brain_version": 85,
                  "auto_release_minutes": 60
                }
              }
            }
          },
          "404": {
            "description": "role contract not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "contract not active, or the role already has an open session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "insufficient_aic",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/session/{id}/release": {
      "post": {
        "tags": [
          "Role contracts"
        ],
        "summary": "Close a role session with a summary",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "summary": {
                    "type": "string",
                    "description": "What was done, what is pending, what comes next. Max 4000 chars"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Released",
            "content": {
              "application/json": {
                "example": {
                  "session_id": 24,
                  "release_type": "manual",
                  "duration_seconds": 34
                }
              }
            }
          },
          "404": {
            "description": "session not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "session already released",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/session/{id}": {
      "get": {
        "tags": [
          "Role contracts"
        ],
        "summary": "Read one session and everything it was pinned to",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Session"
          },
          "404": {
            "description": "session not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/project/{name}/sessions": {
      "get": {
        "tags": [
          "Role contracts"
        ],
        "summary": "Audit trail of a project's role sessions",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sessions: who assumed which role, on what platform, how it closed"
          }
        }
      }
    },
    "/v1/org": {
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Create an organization",
        "description": "Authenticated with your personal key. One key can own one organization. It starts `suspended`: reads work, issuing keys and inviting return 402 until it is activated.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "org_slug": {
                    "type": "string",
                    "description": "3-40 chars: lowercase, digits and hyphens; cannot start or end with a hyphen"
                  },
                  "name": {
                    "type": "string"
                  },
                  "owner_email": {
                    "type": "string",
                    "description": "Becomes the first member, role owner"
                  },
                  "tier": {
                    "type": "string",
                    "description": "team or enterprise"
                  }
                },
                "required": [
                  "org_slug",
                  "name",
                  "owner_email"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created",
            "content": {
              "application/json": {
                "example": {
                  "org_id": 3,
                  "org_slug": "acme-labs",
                  "tier": "team"
                }
              }
            }
          },
          "400": {
            "description": "Invalid slug or missing field",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "This key already owns an organization",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "Read status and plan",
        "responses": {
          "200": {
            "description": "Organization"
          },
          "401": {
            "description": "no_org_context",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/org/invite": {
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Add a member",
        "description": "Requires the `members` permission.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "role": {
                    "type": "string",
                    "description": "owner, admin, developer or viewer"
                  }
                },
                "required": [
                  "email"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Member added"
          },
          "400": {
            "description": "valid email required",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "organization_not_active",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "forbidden — your role lacks the permission",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Already a member",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/org/members": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "List members",
        "responses": {
          "200": {
            "description": "Members"
          }
        }
      }
    },
    "/v1/org/members/{id}/role": {
      "patch": {
        "tags": [
          "Organizations"
        ],
        "summary": "Change a member's role",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "role": {
                    "type": "string",
                    "description": "owner, admin, developer or viewer"
                  }
                },
                "required": [
                  "role"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Role changed"
          },
          "400": {
            "description": "invalid role",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "member not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "cannot demote the last owner",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/org/members/{id}": {
      "delete": {
        "tags": [
          "Organizations"
        ],
        "summary": "Remove a member and revoke their keys",
        "description": "What the member already wrote stays in the organization, attributed to them. Access ends; history does not.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Removed",
            "content": {
              "application/json": {
                "example": {
                  "member_id": 4,
                  "status": "removed",
                  "keys_revoked": 1
                }
              }
            }
          },
          "404": {
            "description": "member not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "cannot remove an owner — change their role first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/org/keys": {
      "post": {
        "tags": [
          "Organizations"
        ],
        "summary": "Issue a member or project key",
        "description": "The full key is returned ONCE in this response and never again; afterwards only its 12-character prefix is shown. A member holds one active key at a time.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "description": "member or project"
                  },
                  "member_id": {
                    "type": "integer",
                    "description": "Required for type=member"
                  },
                  "project_slug": {
                    "type": "string",
                    "description": "Required for type=project"
                  },
                  "role_cap": {
                    "type": "string",
                    "description": "Ceiling on what the key can do"
                  },
                  "expires_days": {
                    "type": "integer",
                    "description": "Expiry, for project keys"
                  }
                },
                "required": [
                  "type"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Key issued",
            "content": {
              "application/json": {
                "example": {
                  "api_key": "aicm_...",
                  "type": "member",
                  "key_prefix": "aicm_d4e133d"
                }
              }
            }
          },
          "402": {
            "description": "project key limit reached for your plan",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Member already has an active key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "List active keys (prefix only)",
        "responses": {
          "200": {
            "description": "Keys"
          }
        }
      }
    },
    "/v1/org/keys/{id}": {
      "delete": {
        "tags": [
          "Organizations"
        ],
        "summary": "Revoke a key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked"
          },
          "404": {
            "description": "key not found or not active",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/memory/{id}/verify": {
      "get": {
        "tags": [
          "Verification"
        ],
        "summary": "Read a memory's on-chain record",
        "security": [],
        "description": "Public, no API key. Reads the record straight from the memory contract. The `id` is the GLOBAL on-chain id (`chain_memory_id`), not your `memory_number`: get it from /v1/my/memory/{seq}/proof. Returns the event hash and metadata. The ciphertext is not returned by this endpoint (since 2026-09-14); it remains stored on-chain and anyone can read it with `getMemory()` through the public RPC, but it cannot be decrypted without the owner's key.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "chain_memory_id"
          }
        ],
        "responses": {
          "200": {
            "description": "On-chain record",
            "content": {
              "application/json": {
                "example": {
                  "memory_id": 664,
                  "ai_id": 1,
                  "category": "DECISION",
                  "summary_length": 3632,
                  "event_hash": "0x2b6f51ac…",
                  "encrypted_summary_size": 3660,
                  "timestamp": 1757780000,
                  "importance": 8,
                  "is_sealed": false,
                  "contract": "0x…",
                  "chain_id": 202604
                }
              }
            }
          },
          "400": {
            "description": "invalid memory id",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "memory not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/my/memory/{seq}/proof": {
      "get": {
        "tags": [
          "Verification"
        ],
        "summary": "Get a shareable anchoring proof for one of your memories",
        "description": "Starts from your `memory_number` and returns the coordinates a third party needs to check the anchor on-chain without your API key. While the memory is not yet synced (~30 s after writing), `anchored` is false and the hash fields are null.",
        "parameters": [
          {
            "name": "seq",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "memory_number"
          }
        ],
        "responses": {
          "200": {
            "description": "Proof",
            "content": {
              "application/json": {
                "example": {
                  "memory_number": 489,
                  "event_hash": "0x…",
                  "anchored": true,
                  "chain_memory_id": 664,
                  "chain_tx": "0x…",
                  "batch_root": null,
                  "chain_id": 202604,
                  "contract": "0x…"
                }
              }
            }
          },
          "404": {
            "description": "memory not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/verify-memory": {
      "post": {
        "tags": [
          "Verification"
        ],
        "summary": "Verify a memory's Merkle inclusion",
        "security": [],
        "description": "Public, no API key. POST only: a GET returns 404. Mode `stored` looks the proof up by itself from `memory_id` or `event_hash` and reports whether the batch was anchored. Modes `light` and `full` (default when `mode` is omitted) verify a proof the client supplies and require `event_hash`, `proof` and `batch_root`. Only hashes and metadata are returned, never content.\n\nIMPORTANT: a Merkle proof exists only for memories that went through a compression batch (POST /v1/compress). Every other memory returns `valid: false` with `anchor_status: \"uncompressed\"` — that does NOT mean it is unanchored. Each memory is written to the memory contract individually when it syncs; check that with /v1/my/memory/{seq}/proof and /v1/memory/{id}/verify.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "stored",
                      "light",
                      "full"
                    ],
                    "description": "Default: full"
                  },
                  "memory_id": {
                    "type": "integer",
                    "description": "mode=stored: internal memory id"
                  },
                  "event_hash": {
                    "type": "string",
                    "description": "mode=stored alternative to memory_id; required for light/full"
                  },
                  "proof": {
                    "description": "light/full: Merkle proof"
                  },
                  "batch_root": {
                    "type": "string",
                    "description": "light/full"
                  },
                  "memory": {
                    "description": "full: the memory object"
                  },
                  "content_hash": {
                    "type": "string",
                    "description": "full"
                  },
                  "daily_root": {
                    "type": "string",
                    "description": "full"
                  }
                }
              },
              "example": {
                "mode": "stored",
                "event_hash": "0x2b6f51ac…"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Result. For mode=stored the shape is shown; light/full return the verifier's own result object.",
            "content": {
              "application/json": {
                "example": {
                  "valid": true,
                  "found": true,
                  "mode": "stored",
                  "memory_id": 612,
                  "event_hash": "0x…",
                  "batch_root": "0x…",
                  "batch_id": 41,
                  "leaf_index": 3,
                  "anchor_status": "anchored",
                  "checkpoint_id": 12,
                  "checkpoint_tx_hash": "0x…",
                  "block_number": 80467,
                  "verified_at": 1757780000,
                  "merkle_version": "v2"
                }
              }
            }
          },
          "400": {
            "description": "Missing fields for the chosen mode",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "error": "event_hash, proof and batch_root required for mode=light/full"
                }
              }
            }
          },
          "404": {
            "description": "mode=stored: memory not found",
            "content": {
              "application/json": {
                "example": {
                  "valid": false,
                  "found": false,
                  "reason": "Memory not found"
                }
              }
            }
          }
        }
      }
    },
    "/v1/verify/{name}": {
      "get": {
        "tags": [
          "Verification"
        ],
        "summary": "List a project's on-chain state anchors",
        "security": [],
        "description": "Public, no API key. Returns every anchored version of a Project Brain with its `state_hash` and on-chain coordinates in the ProjectStateAnchor contract, never the state itself. To verify: `getAnchorById(anchor_id)` returns (projectId, version, stateHash, anchoredAt, anchoredBy); `stateHash` must equal `state_hash` for that version.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Project slug"
          }
        ],
        "responses": {
          "200": {
            "description": "Anchors",
            "content": {
              "application/json": {
                "example": {
                  "project": "chainmemory",
                  "chain": {
                    "chainId": 202604,
                    "rpc": "https://rpc.chainmemory.ai",
                    "contract": "0xa7A8BA51950255b3e223a6745597C67009Fe7875",
                    "contract_name": "ProjectStateAnchor"
                  },
                  "latest_version": 86,
                  "latest_state_hash": "0x…",
                  "anchors": [
                    {
                      "version": 86,
                      "state_hash": "0x…",
                      "anchor_id": 90,
                      "tx_hash": "0x…",
                      "block_number": 678000,
                      "anchored_at": "2026-09-12T16:00:00.000Z",
                      "status": "anchored"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/inject": {
      "post": {
        "tags": [
          "Context injection"
        ],
        "summary": "Inject memories into a conversation",
        "description": "Returns the selected memories plus `inject_text`, a ready-to-paste block that tells the model to cite each memory by its #N. Fee: 0.1 AIC (half burned, half to treasury). Quarantined and archived memories are skipped silently. With `optimistic: true` the response comes back immediately and the payment settles in the background: poll /v1/inject/status/{id} with `payment.inject_log_id`. Sealed memories come back as ciphertext (`content_blob`, `scheme` 2) and as a placeholder inside `inject_text`; the client decrypts them.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "memory_ids": {
                    "type": "array",
                    "items": {
                      "type": "integer"
                    },
                    "description": "memory_numbers, 1-50"
                  },
                  "optimistic": {
                    "type": "boolean",
                    "description": "Respond before the payment confirms"
                  },
                  "project_filter": {
                    "type": "string",
                    "description": "Recorded in the history only"
                  },
                  "target_platform": {
                    "type": "string",
                    "description": "Recorded in the history only"
                  }
                },
                "required": [
                  "memory_ids"
                ]
              },
              "example": {
                "memory_ids": [
                  612,
                  613
                ],
                "optimistic": true,
                "target_platform": "claude"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Injected",
            "content": {
              "application/json": {
                "example": {
                  "injected": 2,
                  "memories": [
                    {
                      "id": 740,
                      "user_seq": 612,
                      "summary": "…",
                      "category": "DECISION",
                      "importance": 8,
                      "timestamp": 1757780000,
                      "tags": [
                        "chainmemory"
                      ],
                      "event_hash": "0x…",
                      "chain_memory_id": 664,
                      "content_blob": null,
                      "scheme": null,
                      "plain_len": null
                    }
                  ],
                  "inject_text": "[Estas memorias son verificables on-chain…]\n\n[Memoria #612 · ref:740 · chain:664 · DECISION · verificable on-chain]\n…",
                  "payment": {
                    "charged_aic": "0.1",
                    "status": "pending",
                    "inject_log_id": 51
                  },
                  "stats": {
                    "memory_count": 2,
                    "total_chars": 1840,
                    "estimated_tokens": 460
                  }
                }
              }
            }
          },
          "400": {
            "description": "memory_ids missing or more than 50",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "402": {
            "description": "insufficient_aic",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "error": "insufficient_aic",
                  "required_aic": "0.1",
                  "faucet_url": "https://faucet.chainmemory.ai"
                }
              }
            }
          },
          "404": {
            "description": "none of the ids is a valid memory of this key",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "payment_failed (non-optimistic mode only)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/inject/balance": {
      "get": {
        "tags": [
          "Context injection"
        ],
        "summary": "Balance and injections left",
        "responses": {
          "200": {
            "description": "Balance",
            "content": {
              "application/json": {
                "example": {
                  "wallet": "0x…",
                  "balance_wei": "4200000000000000000",
                  "balance_aic": "4.2",
                  "cost_per_inject_aic": "0.1",
                  "injects_remaining": "42",
                  "faucet_url": "https://faucet.chainmemory.ai"
                }
              }
            }
          }
        }
      }
    },
    "/v1/inject/status/{id}": {
      "get": {
        "tags": [
          "Context injection"
        ],
        "summary": "Payment status of an optimistic injection",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "payment.inject_log_id"
          }
        ],
        "responses": {
          "200": {
            "description": "Status",
            "content": {
              "application/json": {
                "example": {
                  "id": 51,
                  "status": "confirmed",
                  "tx_hash": "0x…,0x…",
                  "timestamp": 1757780000
                }
              }
            }
          },
          "404": {
            "description": "not found",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/inject/history": {
      "get": {
        "tags": [
          "Context injection"
        ],
        "summary": "Your injection history",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Max 100, default 20"
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer"
            },
            "description": "Default 0"
          }
        ],
        "responses": {
          "200": {
            "description": "History. `memory_ids` holds internal ids, not memory_numbers; totals count confirmed injections only.",
            "content": {
              "application/json": {
                "example": {
                  "history": [
                    {
                      "id": 51,
                      "memory_ids": [
                        740,
                        741
                      ],
                      "project_filter": null,
                      "memory_count": 2,
                      "token_count": 460,
                      "aic_charged": "0.1",
                      "aic_burned": "0.05",
                      "aic_treasury": "0.05",
                      "tx_hash": "0x…,0x…",
                      "target_platform": "claude",
                      "timestamp": 1757780000,
                      "success": true,
                      "status": "confirmed"
                    }
                  ],
                  "totals": {
                    "total_injects": 1,
                    "total_memories_injected": 2,
                    "total_tokens": 460,
                    "total_aic_spent": "0.100000",
                    "total_aic_burned": "0.050000"
                  },
                  "count": 1
                }
              }
            }
          }
        }
      }
    },
    "/v1/register": {
      "post": {
        "tags": [
          "Account"
        ],
        "summary": "Register your AI identity on-chain",
        "description": "Optional: the first memory sync registers you automatically as `auto-<address>`. Call this first if you want a real name and model on-chain. Once registered, calling it again returns `already_registered` and changes nothing.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "model": {
                    "type": "string"
                  }
                },
                "required": [
                  "name",
                  "model"
                ]
              },
              "example": {
                "name": "research-agent",
                "model": "claude-opus-5"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Registered, or already registered",
            "content": {
              "application/json": {
                "example": {
                  "ai_id": 14,
                  "name": "research-agent",
                  "model": "claude-opus-5",
                  "tx": "0x…"
                }
              }
            }
          },
          "400": {
            "description": "name and model required",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
