{
  "components": {
    "schemas": {
      "action": {
        "additionalProperties": false,
        "description": "What the agent is about to do, or has just done. One shape for both moments. It carries what the content does, never the content.",
        "properties": {
          "account": {
            "type": [
              "string",
              "null"
            ]
          },
          "asserts": {
            "description": "Which of the eight constraints the content exhibits. Names are checked against the eight by the runtime, not here.",
            "items": {
              "type": "string"
            },
            "type": "array",
            "uniqueItems": true
          },
          "channel": {
            "type": "string"
          },
          "cites": {
            "items": {
              "$ref": "#/components/schemas/claim"
            },
            "type": "array"
          },
          "completeness": {
            "enum": [
              "complete",
              "open",
              "vague",
              null
            ]
          },
          "content_ref": {
            "type": [
              "string",
              "null"
            ]
          },
          "depends_on": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "direction": {
            "enum": [
              "outbound",
              "inbound"
            ]
          },
          "discloses": {
            "description": "What the content volunteers that answering did not need: its approval process or authority limits, or its own limits stated as standing facts. Additive and optional within version 1 (DOOR-v1.md rule 2). Names are checked by the runtime, not here (rule 7). A warning, never a reason to stop the message.",
            "items": {
              "type": "string"
            },
            "type": "array",
            "uniqueItems": true
          },
          "due_at": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/timestamp"
              },
              {
                "type": "null"
              }
            ]
          },
          "eligibility": {
            "type": "object"
          },
          "global_parent": {
            "type": [
              "string",
              "null"
            ]
          },
          "intent": {
            "enum": [
              "draft",
              "send",
              "commit"
            ]
          },
          "motion": {
            "type": "string"
          },
          "occurred_at": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/timestamp"
              },
              {
                "type": "null"
              }
            ],
            "description": "When it happened, which is not when we heard of it. Omitted means now."
          },
          "person": {
            "type": [
              "string",
              "null"
            ]
          },
          "relationship_stage": {
            "type": [
              "string",
              "null"
            ]
          },
          "segment": {
            "type": [
              "string",
              "null"
            ]
          },
          "seniority": {
            "type": [
              "string",
              "null"
            ]
          },
          "topic": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/topic"
              },
              {
                "type": "null"
              }
            ]
          },
          "unresolved": {
            "description": "Levels that should have had a value and could not be resolved. Not the same as null, which is not applicable.",
            "items": {
              "enum": [
                "person",
                "working_unit",
                "account",
                "global_parent"
              ]
            },
            "type": "array",
            "uniqueItems": true
          },
          "working_unit": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "motion",
          "channel"
        ],
        "type": "object"
      },
      "claim": {
        "additionalProperties": false,
        "description": "A register claim the content cites, with the state it was in when used.",
        "properties": {
          "entry": {
            "$ref": "#/components/schemas/identifier"
          },
          "status": {
            "enum": [
              "current",
              "stale",
              "contested",
              "unowned"
            ]
          },
          "version": {
            "minimum": 1,
            "type": "integer"
          }
        },
        "required": [
          "entry",
          "status"
        ],
        "type": "object"
      },
      "identifier": {
        "description": "An id that becomes a filename on our side: letters, digits, dot, hyphen and underscore, starting with a letter or digit. Refused here so that a malformed id is a 400 naming the field, rather than reaching a store and becoming a 500 \u2014 which would tell a caller to spool and replay a request that can never succeed. The same constraint appears in every schema here; spec/validate.py checks that they still agree.",
        "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$",
        "type": "string"
      },
      "observe_request": {
        "additionalProperties": false,
        "properties": {
          "acting_person": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/identifier"
              },
              {
                "type": "null"
              }
            ],
            "description": "A person this action is attributable to, where one is. The key authenticates an agent, so a person named here is **asserted, not authenticated** \u2014 the record says which, because an agent naming a person is a claim we cannot check. Their roles are looked up here and stamped on the record; a person we have never heard of is a 400 rather than an attribution to nobody."
          },
          "action": {
            "$ref": "#/components/schemas/action"
          },
          "authority": {
            "additionalProperties": false,
            "description": "What permitted this. Stored, never derived, because the mandate may have changed by the time anybody asks.",
            "properties": {
              "approval_ref": {
                "type": "string"
              },
              "basis": {
                "enum": [
                  "standard_box",
                  "trigger",
                  "exception",
                  "none_required"
                ]
              },
              "exception_ref": {
                "type": "string"
              },
              "trigger": {
                "type": "string"
              }
            },
            "required": [
              "basis"
            ],
            "type": "object"
          },
          "client": {
            "additionalProperties": false,
            "description": "Which library sent this. Support, not identity \u2014 identity is the key.",
            "properties": {
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "content_fingerprint": {
            "type": [
              "string",
              "null"
            ]
          },
          "happened": {
            "description": "Whether the action went out. False records the checks without claiming a touch that did not occur.",
            "type": "boolean"
          },
          "request_version": {
            "$ref": "#/components/schemas/request_version"
          },
          "upstream_source": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "request_version",
          "action"
        ],
        "type": "object"
      },
      "request_version": {
        "const": 1,
        "description": "The version of the request shape, which is not the version of a record. Required on every request and echoed on every response, so neither side has to guess."
      },
      "timestamp": {
        "description": "RFC 3339, UTC, millisecond precision. Trailing Z required \u2014 portability constraint 4.",
        "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$",
        "type": "string"
      },
      "topic": {
        "additionalProperties": false,
        "properties": {
          "text": {
            "minLength": 1,
            "type": "string"
          },
          "topic_ref": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "topic_ref",
          "text"
        ],
        "type": "object"
      },
      "unchecked_request": {
        "additionalProperties": false,
        "description": "A window in which the caller acted and we gave no answer. Only the caller can report it: we were down.",
        "properties": {
          "actions": {
            "minimum": 0,
            "type": "integer"
          },
          "actions_counted": {
            "enum": [
              "exact",
              "at_least"
            ]
          },
          "client": {
            "additionalProperties": false,
            "properties": {
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            },
            "type": "object"
          },
          "detail": {
            "maxLength": 200,
            "type": "string"
          },
          "reason": {
            "enum": [
              "unreachable",
              "timed_out",
              "server_error",
              "unknown"
            ]
          },
          "request_version": {
            "$ref": "#/components/schemas/request_version"
          },
          "window_end": {
            "$ref": "#/components/schemas/timestamp"
          },
          "window_start": {
            "$ref": "#/components/schemas/timestamp"
          }
        },
        "required": [
          "request_version",
          "window_start",
          "window_end",
          "reason",
          "actions",
          "actions_counted"
        ],
        "type": "object"
      }
    }
  },
  "info": {
    "description": "What an outside agent may send Reynforce over JSON, generated from `spec/api/request.schema.json` and the door's own specification rather than written.\n\n**No host answers this door today.** A customer's deployment serves the register screen and an MCP endpoint at `/mcp`; this is the specification and the library behind it. The connection to make is the MCP one \u2014 see https://reynforce.com/docs.html.\n\n**The request bodies here are normative and the response is not.** The 200 carries the specification's own worked example rather than a schema, because the repository has deliberately not made the response shape normative and a schema invented here would be one somebody held us to.",
    "title": "The Reynforce HTTP door",
    "version": "1"
  },
  "openapi": "3.1.0",
  "paths": {
    "/v1/health": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "checked_at": "2026-09-17T09:14:02.412Z",
                  "findings": [
                    {
                      "code": "\u2026",
                      "outcome": "\u2026",
                      "subject": "\u2026",
                      "why": "\u2026"
                    }
                  ],
                  "not_checked": [
                    "triggers: what leaves the standard box",
                    "budgets: what the action spends"
                  ],
                  "observation_only": true,
                  "outcome": "allowed",
                  "recorded": [
                    "touch",
                    "resource_draw",
                    "constraint_check"
                  ],
                  "request_version": 1,
                  "required": []
                }
              }
            },
            "description": "The decision. `observation_only` says the answer is advisory today, `not_checked` names what was not evaluated rather than letting `allowed` imply it passed, and `recorded` is the kinds of record the call produced."
          },
          "400": {
            "description": "The body does not match the schema, or the action names a motion, channel or constraint that does not exist. The reason says which."
          },
          "401": {
            "description": "The key is not usable. One sentence, whatever is wrong with it."
          },
          "404": {
            "description": "No such path."
          },
          "409": {
            "description": "Somebody else changed something this write depended on."
          },
          "413": {
            "description": "The body is larger than the cap."
          },
          "415": {
            "description": "Not `application/json`."
          },
          "422": {
            "description": "The request is well-formed but there is nothing to check it against \u2014 most often: this agent has no mandate."
          },
          "500": {
            "description": "Something went wrong on our side. The reason is deliberately not the exception's words: a stack trace on the wire tells a caller about our insides rather than about their request."
          },
          "503": {
            "description": "We are up but cannot record. The caller should spool and report the window."
          }
        },
        "summary": "Whether we are up, and which request versions we speak"
      }
    },
    "/v1/observe": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/observe_request"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "checked_at": "2026-09-17T09:14:02.412Z",
                  "findings": [
                    {
                      "code": "\u2026",
                      "outcome": "\u2026",
                      "subject": "\u2026",
                      "why": "\u2026"
                    }
                  ],
                  "not_checked": [
                    "triggers: what leaves the standard box",
                    "budgets: what the action spends"
                  ],
                  "observation_only": true,
                  "outcome": "allowed",
                  "recorded": [
                    "touch",
                    "resource_draw",
                    "constraint_check"
                  ],
                  "request_version": 1,
                  "required": []
                }
              }
            },
            "description": "The decision. `observation_only` says the answer is advisory today, `not_checked` names what was not evaluated rather than letting `allowed` imply it passed, and `recorded` is the kinds of record the call produced."
          },
          "400": {
            "description": "The body does not match the schema, or the action names a motion, channel or constraint that does not exist. The reason says which."
          },
          "401": {
            "description": "The key is not usable. One sentence, whatever is wrong with it."
          },
          "404": {
            "description": "No such path."
          },
          "409": {
            "description": "Somebody else changed something this write depended on."
          },
          "413": {
            "description": "The body is larger than the cap."
          },
          "415": {
            "description": "Not `application/json`."
          },
          "422": {
            "description": "The request is well-formed but there is nothing to check it against \u2014 most often: this agent has no mandate."
          },
          "500": {
            "description": "Something went wrong on our side. The reason is deliberately not the exception's words: a stack trace on the wire tells a caller about our insides rather than about their request."
          },
          "503": {
            "description": "We are up but cannot record. The caller should spool and report the window."
          }
        },
        "summary": "An action, and what its content does"
      }
    },
    "/v1/unchecked": {
      "post": {
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/unchecked_request"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "example": {
                  "checked_at": "2026-09-17T09:14:02.412Z",
                  "findings": [
                    {
                      "code": "\u2026",
                      "outcome": "\u2026",
                      "subject": "\u2026",
                      "why": "\u2026"
                    }
                  ],
                  "not_checked": [
                    "triggers: what leaves the standard box",
                    "budgets: what the action spends"
                  ],
                  "observation_only": true,
                  "outcome": "allowed",
                  "recorded": [
                    "touch",
                    "resource_draw",
                    "constraint_check"
                  ],
                  "request_version": 1,
                  "required": []
                }
              }
            },
            "description": "The decision. `observation_only` says the answer is advisory today, `not_checked` names what was not evaluated rather than letting `allowed` imply it passed, and `recorded` is the kinds of record the call produced."
          },
          "400": {
            "description": "The body does not match the schema, or the action names a motion, channel or constraint that does not exist. The reason says which."
          },
          "401": {
            "description": "The key is not usable. One sentence, whatever is wrong with it."
          },
          "404": {
            "description": "No such path."
          },
          "409": {
            "description": "Somebody else changed something this write depended on."
          },
          "413": {
            "description": "The body is larger than the cap."
          },
          "415": {
            "description": "Not `application/json`."
          },
          "422": {
            "description": "The request is well-formed but there is nothing to check it against \u2014 most often: this agent has no mandate."
          },
          "500": {
            "description": "Something went wrong on our side. The reason is deliberately not the exception's words: a stack trace on the wire tells a caller about our insides rather than about their request."
          },
          "503": {
            "description": "We are up but cannot record. The caller should spool and report the window."
          }
        },
        "summary": "A window in which the caller acted and we gave no answer"
      }
    }
  }
}
