{
  "openapi": "3.1.0",
  "info": {
    "title": "Termn API",
    "version": "1.0.0",
    "description": "Termn coordinates agreements and the money that moves against them.\n\nA **workspace** is one deal or matter. It holds **agreements**, each of which has\nordered **stages**, and each stage has **gates** — the conditions that must be\nsatisfied before the stage's actions run. A gate names who it is waiting on. The\n**ledger** flattens all of it into one row per thing you are owed or owe.\n\n### The one idea worth internalising\n\nMoney that a sender *says* they sent is not money that *arrived*. Those are two\ndifferent facts and this API never collapses them:\n\n- `state: \"sender_reported\"` means someone claimed they sent it.\n- `state: \"reconciled\"` — and the `reconciled` boolean — means receiver-side\n  evidence confirmed it arrived.\n\nNever treat the first as the second. Termn never holds, initiates, or redirects\nfunds; it records evidence about transfers that happen between real banks.\n\n### Reading a status\n\nFields ending in `_sentence`, plus `status` on an agreement and `waiting_on` on a\ngate, are plain-language text for display. They are written for humans and change\nfreely — never parse them. Branch on `lifecycle`, `state`, `phase`, and `status`\nenums instead.\n\n### Authentication\n\nSend an organization token as `Authorization: Bearer <token>`. Issue tokens in the\nTermn app under Settings → Developers. Every response is scoped to the token's\norganization; another organization's record returns `404`, never `403`.\n\nTokens carry a scope. A `read_only` token returns `403` on any write.\n\n### Lists\n\nEvery list returns `{\"data\": [...], \"pagination\": {...}}`. Check\n`pagination.total` — never assume a short page means you have everything. `limit`\nis capped at 200.\n\n### Retries\n\nSend an `Idempotency-Key` header on any `POST`. Repeating a key replays the\noriginal response instead of creating a second record. Reusing a key with a\ndifferent body returns `409`.\n\n### Errors\n\nEvery failure returns the same envelope with a stable `error.code` and an\n`error.request_id` you can quote to support:\n\n```json\n{\"error\": {\"code\": \"not_found\", \"message\": \"Contact not found.\",\n           \"status\": 404, \"request_id\": \"9f2c…\"}}\n```\n\nValidation failures add `error.errors[]`, each naming a `field` and a `code`.\n\n### Enumerated values\n\n**Contact kind**\n- `person` — Person\n- `household` — Household\n- `trust` — Trust\n- `llc` — LLC\n- `corporation` — Corporation\n- `partnership` — Partnership\n- `nonprofit` — Nonprofit\n- `other` — Other entity\n\n**Agreement kind**\n- `general` — Agreement\n- `safe` — SAFE\n\n**Agreement lifecycle**\n- `draft` — Draft\n- `active` — Active\n- `completed` — Completed\n- `cancelled` — Cancelled\n- `expired` — Expired\n\n**Movement state**\n- `draft` — Not set up yet\n- `authorized` — Authorized\n- `instructions_ready` — Instructions available to the sender\n- `sender_reported` — Sender says it was sent\n- `receipt_pending` — Waiting for proof it arrived\n- `processing` — Processing\n- `reconciled` — Confirmed received\n- `exception` — Needs attention\n- `returned` — Returned\n- `cancelled` — Cancelled\n\n**Gate type**\n- `signature_blocks` — Signature details confirmed\n- `document_signed` — Document signed\n- `participant_approval` — Approval\n- `file_upload` — Document or proof uploaded\n- `countdown` — Scheduled date reached\n- `movement_authorized` — Payment authorized\n- `movement_reconciled` — Payment confirmed received\n- `information_request` — Information provided\n- `identity_verified` — Identity verified\n\n**Participant roles**\n- `signer` — Signs\n- `funder` — Sends money\n- `receiver` — Receives money\n- `approver` — Approves\n- `investor` — Investor\n- `issuer` — Issuer\n- `trustee` — Trustee\n- `contributor` — Provides information\n- `observer` — Observer"
  },
  "paths": {
    "/api/v1/me": {
      "get": {
        "operationId": "apps_api_api_whoami",
        "summary": "Describe the calling token",
        "parameters": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IdentityOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Return the organization this token belongs to, its scope, and its expiry.\n\nCall this once at startup to key your local records off `organization_id`.",
        "tags": [
          "Identity"
        ]
      }
    },
    "/api/v1/contacts": {
      "get": {
        "operationId": "apps_api_api_list_contacts",
        "summary": "List contacts",
        "parameters": [
          {
            "in": "query",
            "name": "search",
            "schema": {
              "default": "",
              "description": "Case-insensitive match on name or email.",
              "maxLength": 200,
              "title": "Search",
              "type": "string"
            },
            "required": false,
            "description": "Case-insensitive match on name or email."
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 50,
              "maximum": 200,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            },
            "required": false
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            },
            "required": false
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_ContactOut_"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "People and entities your organization does business with. Reusable across\nworkspaces and agreements — participants reference contacts, never copy them.",
        "tags": [
          "Contacts"
        ]
      },
      "post": {
        "operationId": "apps_api_api_create_contact_endpoint",
        "summary": "Create a contact",
        "parameters": [],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This token is read-only.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Create a contact. Send an `Idempotency-Key` header to make retries safe —\nrepeating a key replays the original response instead of creating a duplicate.",
        "tags": [
          "Contacts"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactIn"
              }
            }
          },
          "required": true
        }
      }
    },
    "/api/v1/contacts/{contact_id}": {
      "get": {
        "operationId": "apps_api_api_get_contact",
        "summary": "Fetch one contact",
        "parameters": [
          {
            "in": "path",
            "name": "contact_id",
            "schema": {
              "format": "uuid",
              "title": "Contact Id",
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "tags": [
          "Contacts"
        ]
      },
      "patch": {
        "operationId": "apps_api_api_patch_contact",
        "summary": "Update a contact",
        "parameters": [
          {
            "in": "path",
            "name": "contact_id",
            "schema": {
              "format": "uuid",
              "title": "Contact Id",
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This token is read-only.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Change only the fields you send. Omitted fields are left alone.",
        "tags": [
          "Contacts"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ContactPatch"
              }
            }
          },
          "required": true
        }
      }
    },
    "/api/v1/workspaces": {
      "get": {
        "operationId": "apps_api_api_list_workspaces",
        "summary": "List workspaces",
        "parameters": [
          {
            "in": "query",
            "name": "search",
            "schema": {
              "default": "",
              "description": "Case-insensitive match on name or internal reference.",
              "maxLength": 200,
              "title": "Search",
              "type": "string"
            },
            "required": false,
            "description": "Case-insensitive match on name or internal reference."
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 50,
              "maximum": 200,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            },
            "required": false
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            },
            "required": false
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_WorkspaceOut_"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "A workspace is the container for one deal or matter. Every agreement\nbelongs to exactly one workspace.",
        "tags": [
          "Workspaces"
        ]
      },
      "post": {
        "operationId": "apps_api_api_create_workspace_endpoint",
        "summary": "Create a workspace",
        "parameters": [],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This token is read-only.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Create an empty workspace. Add agreements to it with `POST /v1/agreements`.",
        "tags": [
          "Workspaces"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkspaceIn"
              }
            }
          },
          "required": true
        }
      }
    },
    "/api/v1/workspaces/{workspace_id}": {
      "get": {
        "operationId": "apps_api_api_get_workspace",
        "summary": "Fetch one workspace",
        "parameters": [
          {
            "in": "path",
            "name": "workspace_id",
            "schema": {
              "format": "uuid",
              "title": "Workspace Id",
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "tags": [
          "Workspaces"
        ]
      }
    },
    "/api/v1/workspaces/{workspace_id}/contacts": {
      "post": {
        "operationId": "apps_api_api_add_workspace_contact",
        "summary": "Reference a contact from a workspace",
        "parameters": [
          {
            "in": "path",
            "name": "workspace_id",
            "schema": {
              "format": "uuid",
              "title": "Workspace Id",
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkspaceOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This token is read-only.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Associate an existing contact with a workspace so it appears in that\nworkspace's people list.",
        "tags": [
          "Workspaces"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkspaceContactIn"
              }
            }
          },
          "required": true
        }
      }
    },
    "/api/v1/agreements": {
      "get": {
        "operationId": "apps_api_api_list_agreements",
        "summary": "List agreements",
        "parameters": [
          {
            "in": "query",
            "name": "workspace_id",
            "schema": {
              "anyOf": [
                {
                  "format": "uuid",
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Workspace Id"
            },
            "required": false
          },
          {
            "in": "query",
            "name": "lifecycle",
            "schema": {
              "default": "",
              "description": "Filter by machine lifecycle, e.g. `active`.",
              "title": "Lifecycle",
              "type": "string"
            },
            "required": false,
            "description": "Filter by machine lifecycle, e.g. `active`."
          },
          {
            "in": "query",
            "name": "search",
            "schema": {
              "default": "",
              "description": "Case-insensitive match on the agreement name.",
              "maxLength": 250,
              "title": "Search",
              "type": "string"
            },
            "required": false,
            "description": "Case-insensitive match on the agreement name."
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 50,
              "maximum": 200,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            },
            "required": false
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            },
            "required": false
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_AgreementOut_"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Agreements, each with its ordered stages (shown as “steps” in the Termn\napp) and the gates holding them up. `stages[].gates[].waiting_on_participant_ids`\nis what you need to build a per-person task list. `publishable` and\n`publish_blockers` are always null in lists — fetch one agreement to get\ndraft readiness.",
        "tags": [
          "Agreements"
        ]
      },
      "post": {
        "operationId": "apps_api_api_create_agreement_endpoint",
        "summary": "Create a draft agreement",
        "parameters": [],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgreementOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This token is read-only.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Create a draft agreement inside a workspace.\n\nDrafting is always free and sends nothing to anyone. Publishing an agreement\n— the first participant-facing effect — happens in the Termn app, not here.",
        "tags": [
          "Agreements"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgreementIn"
              }
            }
          },
          "required": true
        }
      }
    },
    "/api/v1/agreements/{agreement_id}": {
      "get": {
        "operationId": "apps_api_api_get_agreement_endpoint",
        "summary": "Fetch one agreement",
        "parameters": [
          {
            "in": "path",
            "name": "agreement_id",
            "schema": {
              "format": "uuid",
              "title": "Agreement Id",
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgreementOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "One agreement in full, including `publishable` and `publish_blockers`\nwhile it is a draft — check these before proposing to publish.",
        "tags": [
          "Agreements"
        ]
      }
    },
    "/api/v1/agreements/{agreement_id}/information": {
      "get": {
        "operationId": "apps_api_api_list_information_endpoint",
        "summary": "Answers to an agreement's information requests",
        "parameters": [
          {
            "in": "path",
            "name": "agreement_id",
            "schema": {
              "format": "uuid",
              "title": "Agreement Id",
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "items": {
                    "$ref": "#/components/schemas/InformationSubmissionOut"
                  },
                  "title": "Response",
                  "type": "array"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Every sent answer on this agreement, per participant and gate.\n\nThe questions themselves ride on the agreement's information-request gates\n(`stages[].gates[].fields`). Unsent drafts never appear here, and answers only\nclear a gate once someone in the organization has read and accepted them in\nthe Termn app — this connection can neither answer nor accept (invariant 20).",
        "tags": [
          "Agreements"
        ]
      }
    },
    "/api/v1/agreements/{agreement_id}/participants": {
      "post": {
        "operationId": "apps_api_api_add_participant_endpoint",
        "summary": "Add a participant to an agreement",
        "parameters": [
          {
            "in": "path",
            "name": "agreement_id",
            "schema": {
              "format": "uuid",
              "title": "Agreement Id",
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ParticipantOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This token is read-only.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Attach an existing contact to an agreement in one or more roles.\n\nAdding the same contact again merges the new roles into the existing\nparticipant rather than creating a second one.",
        "tags": [
          "Agreements"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ParticipantIn"
              }
            }
          },
          "required": true
        }
      }
    },
    "/api/v1/movements": {
      "get": {
        "operationId": "apps_api_api_list_movements",
        "summary": "List money movements",
        "parameters": [
          {
            "in": "query",
            "name": "state",
            "schema": {
              "default": "",
              "description": "Filter by machine state, e.g. `reconciled`.",
              "title": "State",
              "type": "string"
            },
            "required": false,
            "description": "Filter by machine state, e.g. `reconciled`."
          },
          {
            "in": "query",
            "name": "agreement_id",
            "schema": {
              "anyOf": [
                {
                  "format": "uuid",
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Agreement Id"
            },
            "required": false
          },
          {
            "in": "query",
            "name": "search",
            "schema": {
              "default": "",
              "description": "Case-insensitive match on purpose, reference, or the agreement's name.",
              "maxLength": 200,
              "title": "Search",
              "type": "string"
            },
            "required": false,
            "description": "Case-insensitive match on purpose, reference, or the agreement's name."
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 50,
              "maximum": 200,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            },
            "required": false
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            },
            "required": false
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_MovementOut_"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Read-only by design: only receiver-side evidence at recipient-confirmed\nassurance or better may reconcile a movement, and a sender's acknowledgement\nnever proves receipt — so those transitions are performed by a person in the\nTermn app and observed here. Reconciled movements carry `confirmed_at`,\n`confirmed_by`, and `confirmation_basis`.",
        "tags": [
          "Money movement"
        ]
      }
    },
    "/api/v1/movements/{movement_id}": {
      "get": {
        "operationId": "apps_api_api_get_movement",
        "summary": "Fetch one money movement",
        "parameters": [
          {
            "in": "path",
            "name": "movement_id",
            "schema": {
              "format": "uuid",
              "title": "Movement Id",
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MovementOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "tags": [
          "Money movement"
        ]
      }
    },
    "/api/v1/ledger": {
      "get": {
        "operationId": "apps_api_api_get_ledger",
        "summary": "List ledger rows",
        "parameters": [
          {
            "in": "query",
            "name": "product",
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "`safe` or `agreement`.",
              "title": "Product"
            },
            "required": false,
            "description": "`safe` or `agreement`."
          },
          {
            "in": "query",
            "name": "workspace_id",
            "schema": {
              "anyOf": [
                {
                  "format": "uuid",
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "description": "Limit to one workspace.",
              "title": "Workspace Id"
            },
            "required": false,
            "description": "Limit to one workspace."
          },
          {
            "in": "query",
            "name": "search",
            "schema": {
              "default": "",
              "description": "Case-insensitive match on titles and counterparties.",
              "maxLength": 200,
              "title": "Search",
              "type": "string"
            },
            "required": false,
            "description": "Case-insensitive match on titles and counterparties."
          },
          {
            "in": "query",
            "name": "include_done",
            "schema": {
              "default": true,
              "title": "Include Done",
              "type": "boolean"
            },
            "required": false
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 50,
              "maximum": 200,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            },
            "required": false
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            },
            "required": false
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_LedgerRowOut_"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "One row per thing you are owed or owe, across every workspace — the\ndashboard view. Rows carry both `amount_display` for rendering and\n`amount_minor` for arithmetic. An agreement and the payments under it are\nseparate rows: amounts ride the payment rows, so never sum a whole page\nexpecting per-agreement totals.",
        "tags": [
          "Ledger"
        ]
      }
    },
    "/api/v1/proposals": {
      "post": {
        "operationId": "apps_api_api_create_proposal",
        "summary": "Propose a protected effect",
        "parameters": [],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProposalOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This token is read-only.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "description": "Ask a member of your organization to publish or cancel an agreement.\n\nNothing happens until a member reviews the proposal at `confirm_url` and\nconfirms it there. Send the `confirm_url` to the human you work for. Check\nprogress with `GET /v1/proposals/{id}`; undecided proposals expire in 72\nhours.\n\nA publish proposal is refused while the draft's configuration still has\n`publish_blockers` (fetch the agreement to see them). The one exception is\nworkspace activation: you may propose before the workspace is activated,\nand the confirming member completes activation on the way. A refused\nproposal records nothing and does not consume an idempotency key — retry\nfreely once the blockers are fixed.",
        "tags": [
          "Proposals"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProposalIn"
              }
            }
          },
          "required": true
        }
      },
      "get": {
        "operationId": "apps_api_api_list_proposals",
        "summary": "List proposals",
        "parameters": [
          {
            "in": "query",
            "name": "state",
            "schema": {
              "default": "",
              "description": "Filter: `pending`, `confirmed`, `declined`, `expired`.",
              "title": "State",
              "type": "string"
            },
            "required": false,
            "description": "Filter: `pending`, `confirmed`, `declined`, `expired`."
          },
          {
            "in": "query",
            "name": "agreement_id",
            "schema": {
              "anyOf": [
                {
                  "format": "uuid",
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Agreement Id"
            },
            "required": false
          },
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "default": 50,
              "maximum": 200,
              "minimum": 1,
              "title": "Limit",
              "type": "integer"
            },
            "required": false
          },
          {
            "in": "query",
            "name": "offset",
            "schema": {
              "default": 0,
              "minimum": 0,
              "title": "Offset",
              "type": "integer"
            },
            "required": false
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Page_ProposalOut_"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "tags": [
          "Proposals"
        ]
      }
    },
    "/api/v1/proposals/{proposal_id}": {
      "get": {
        "operationId": "apps_api_api_get_proposal",
        "summary": "Fetch one proposal",
        "parameters": [
          {
            "in": "path",
            "name": "proposal_id",
            "schema": {
              "format": "uuid",
              "title": "Proposal Id",
              "type": "string"
            },
            "required": true
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProposalOut"
                }
              }
            }
          },
          "401": {
            "description": "Missing, malformed, expired, or revoked token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such record in your organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The request did not pass validation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure on our side.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "tags": [
          "Proposals"
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "IdentityOut": {
        "properties": {
          "organization_id": {
            "description": "Stable id to key your local records off.",
            "format": "uuid",
            "title": "Organization Id",
            "type": "string"
          },
          "organization_name": {
            "title": "Organization Name",
            "type": "string"
          },
          "token_name": {
            "description": "The label given to this token when it was issued.",
            "title": "Token Name",
            "type": "string"
          },
          "scope": {
            "description": "`read_write` or `read_only`. Write calls with a read-only token return 403.",
            "title": "Scope",
            "type": "string"
          },
          "expires_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO 8601. The token stops working at this instant.",
            "title": "Expires At"
          }
        },
        "required": [
          "organization_id",
          "organization_name",
          "token_name",
          "scope",
          "expires_at"
        ],
        "title": "IdentityOut",
        "type": "object"
      },
      "ContactOut": {
        "properties": {
          "id": {
            "format": "uuid",
            "title": "Id",
            "type": "string"
          },
          "display_name": {
            "title": "Display Name",
            "type": "string"
          },
          "kind": {
            "description": "Usually one of the documented contact kinds; treat unknown values as `person`.",
            "title": "Kind",
            "type": "string"
          },
          "email": {
            "title": "Email",
            "type": "string"
          },
          "legal_name": {
            "title": "Legal Name",
            "type": "string"
          },
          "phone": {
            "title": "Phone",
            "type": "string"
          },
          "external_reference": {
            "title": "External Reference",
            "type": "string"
          },
          "created_at": {
            "title": "Created At",
            "type": "string"
          },
          "updated_at": {
            "title": "Updated At",
            "type": "string"
          }
        },
        "required": [
          "id",
          "display_name",
          "kind",
          "email",
          "legal_name",
          "phone",
          "external_reference",
          "created_at",
          "updated_at"
        ],
        "title": "ContactOut",
        "type": "object"
      },
      "PageInfo": {
        "properties": {
          "total": {
            "description": "Total rows matching the filters, ignoring limit and offset.",
            "title": "Total",
            "type": "integer"
          },
          "limit": {
            "description": "Rows requested for this page.",
            "title": "Limit",
            "type": "integer"
          },
          "offset": {
            "description": "Rows skipped before this page.",
            "title": "Offset",
            "type": "integer"
          },
          "has_more": {
            "description": "True when more rows follow this page.",
            "title": "Has More",
            "type": "boolean"
          }
        },
        "required": [
          "total",
          "limit",
          "offset",
          "has_more"
        ],
        "title": "PageInfo",
        "type": "object"
      },
      "Page_ContactOut_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/ContactOut"
            },
            "title": "Data",
            "type": "array"
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo"
          }
        },
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[ContactOut]",
        "type": "object"
      },
      "ContactIn": {
        "properties": {
          "display_name": {
            "description": "How this person or entity is shown.",
            "maxLength": 200,
            "minLength": 1,
            "title": "Display Name",
            "type": "string"
          },
          "kind": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ContactKindEnum"
              }
            ],
            "default": "person"
          },
          "email": {
            "default": "",
            "maxLength": 254,
            "title": "Email",
            "type": "string"
          },
          "legal_name": {
            "default": "",
            "maxLength": 250,
            "minLength": 0,
            "title": "Legal Name",
            "type": "string"
          },
          "legal_address": {
            "default": "",
            "maxLength": 500,
            "minLength": 0,
            "title": "Legal Address",
            "type": "string"
          },
          "phone": {
            "default": "",
            "maxLength": 40,
            "minLength": 0,
            "title": "Phone",
            "type": "string"
          },
          "notes": {
            "default": "",
            "maxLength": 2000,
            "minLength": 0,
            "title": "Notes",
            "type": "string"
          },
          "external_reference": {
            "default": "",
            "description": "Your own id for this contact. Use it to re-sync without duplicating.",
            "maxLength": 120,
            "minLength": 0,
            "title": "External Reference",
            "type": "string"
          }
        },
        "required": [
          "display_name"
        ],
        "title": "ContactIn",
        "type": "object"
      },
      "ContactKindEnum": {
        "enum": [
          "person",
          "household",
          "trust",
          "llc",
          "corporation",
          "partnership",
          "nonprofit",
          "other"
        ],
        "title": "ContactKindEnum",
        "type": "string"
      },
      "ContactPatch": {
        "description": "Only the fields you send are changed.",
        "properties": {
          "display_name": {
            "anyOf": [
              {
                "maxLength": 200,
                "minLength": 1,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Display Name"
          },
          "kind": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ContactKindEnum"
              },
              {
                "type": "null"
              }
            ]
          },
          "email": {
            "anyOf": [
              {
                "maxLength": 254,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Email"
          },
          "legal_name": {
            "anyOf": [
              {
                "maxLength": 250,
                "minLength": 0,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Legal Name"
          },
          "legal_address": {
            "anyOf": [
              {
                "maxLength": 500,
                "minLength": 0,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Legal Address"
          },
          "phone": {
            "anyOf": [
              {
                "maxLength": 40,
                "minLength": 0,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Phone"
          },
          "notes": {
            "anyOf": [
              {
                "maxLength": 2000,
                "minLength": 0,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Notes"
          },
          "external_reference": {
            "anyOf": [
              {
                "maxLength": 120,
                "minLength": 0,
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "External Reference"
          }
        },
        "title": "ContactPatch",
        "type": "object"
      },
      "Page_WorkspaceOut_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/WorkspaceOut"
            },
            "title": "Data",
            "type": "array"
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo"
          }
        },
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[WorkspaceOut]",
        "type": "object"
      },
      "WorkspaceOut": {
        "properties": {
          "id": {
            "format": "uuid",
            "title": "Id",
            "type": "string"
          },
          "app_url": {
            "description": "Where a member of your organization opens this workspace in the Termn app (activation happens there).",
            "title": "App Url",
            "type": "string"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "internal_reference": {
            "title": "Internal Reference",
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/WorkspaceStatusEnum"
          },
          "active": {
            "description": "True once the workspace activation has been paid for.",
            "title": "Active",
            "type": "boolean"
          },
          "agreement_count": {
            "title": "Agreement Count",
            "type": "integer"
          },
          "created_at": {
            "title": "Created At",
            "type": "string"
          },
          "updated_at": {
            "title": "Updated At",
            "type": "string"
          }
        },
        "required": [
          "id",
          "app_url",
          "name",
          "internal_reference",
          "status",
          "active",
          "agreement_count",
          "created_at",
          "updated_at"
        ],
        "title": "WorkspaceOut",
        "type": "object"
      },
      "WorkspaceStatusEnum": {
        "enum": [
          "open",
          "archived"
        ],
        "title": "WorkspaceStatusEnum",
        "type": "string"
      },
      "WorkspaceIn": {
        "properties": {
          "name": {
            "maxLength": 200,
            "minLength": 1,
            "title": "Name",
            "type": "string"
          },
          "internal_reference": {
            "default": "",
            "description": "Your own reference for this workspace.",
            "maxLength": 120,
            "minLength": 0,
            "title": "Internal Reference",
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "WorkspaceIn",
        "type": "object"
      },
      "WorkspaceContactIn": {
        "properties": {
          "contact_id": {
            "format": "uuid",
            "title": "Contact Id",
            "type": "string"
          },
          "label": {
            "default": "",
            "maxLength": 120,
            "minLength": 0,
            "title": "Label",
            "type": "string"
          }
        },
        "required": [
          "contact_id"
        ],
        "title": "WorkspaceContactIn",
        "type": "object"
      },
      "AgreementKindEnum": {
        "enum": [
          "general",
          "safe"
        ],
        "title": "AgreementKindEnum",
        "type": "string"
      },
      "AgreementOut": {
        "properties": {
          "id": {
            "format": "uuid",
            "title": "Id",
            "type": "string"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "kind": {
            "$ref": "#/components/schemas/AgreementKindEnum"
          },
          "lifecycle": {
            "$ref": "#/components/schemas/LifecycleEnum",
            "description": "Machine state. Use this, not `status`, for logic."
          },
          "status": {
            "description": "Plain-language sentence for display. Never parse it.",
            "title": "Status",
            "type": "string"
          },
          "workspace_id": {
            "format": "uuid",
            "title": "Workspace Id",
            "type": "string"
          },
          "workspace_name": {
            "title": "Workspace Name",
            "type": "string"
          },
          "participants": {
            "items": {
              "$ref": "#/components/schemas/ParticipantOut"
            },
            "title": "Participants",
            "type": "array"
          },
          "stages": {
            "description": "The agreement's ordered steps (shown as “steps” in the Termn app). A draft needs at least one before it can be published; steps are set up in the app.",
            "items": {
              "$ref": "#/components/schemas/StageOut"
            },
            "title": "Stages",
            "type": "array"
          },
          "publishable": {
            "anyOf": [
              {
                "type": "boolean"
              },
              {
                "type": "null"
              }
            ],
            "description": "Draft agreements only, and only when fetching one agreement: true when nothing blocks publishing. `null` in lists and once no longer a draft.",
            "title": "Publishable"
          },
          "publish_blockers": {
            "anyOf": [
              {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Draft agreements only, and only when fetching one agreement: everything standing between this draft and being sent, including workspace activation.",
            "title": "Publish Blockers"
          },
          "app_url": {
            "description": "Where a member of your organization opens this agreement in the Termn app.",
            "title": "App Url",
            "type": "string"
          },
          "created_at": {
            "title": "Created At",
            "type": "string"
          },
          "updated_at": {
            "title": "Updated At",
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "kind",
          "lifecycle",
          "status",
          "workspace_id",
          "workspace_name",
          "participants",
          "stages",
          "publishable",
          "publish_blockers",
          "app_url",
          "created_at",
          "updated_at"
        ],
        "title": "AgreementOut",
        "type": "object"
      },
      "GateOut": {
        "properties": {
          "id": {
            "format": "uuid",
            "title": "Id",
            "type": "string"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/GateTypeEnum"
          },
          "status": {
            "$ref": "#/components/schemas/GateStatusEnum"
          },
          "waiting_on": {
            "description": "Display text naming who this gate waits on. Never parse it.",
            "title": "Waiting On",
            "type": "string"
          },
          "waiting_on_participant_ids": {
            "description": "The participants this gate is waiting on, so you can build a per-person task list.",
            "items": {
              "format": "uuid",
              "type": "string"
            },
            "title": "Waiting On Participant Ids",
            "type": "array"
          },
          "fields": {
            "anyOf": [
              {
                "items": {
                  "$ref": "#/components/schemas/InformationFieldOut"
                },
                "type": "array"
              },
              {
                "type": "null"
              }
            ],
            "description": "Information-request gates only: the questions being asked. Answers are fetched with the agreement's information endpoint.",
            "title": "Fields"
          }
        },
        "required": [
          "id",
          "name",
          "type",
          "status",
          "waiting_on",
          "waiting_on_participant_ids"
        ],
        "title": "GateOut",
        "type": "object"
      },
      "GateStatusEnum": {
        "enum": [
          "pending",
          "complete",
          "blocked"
        ],
        "title": "GateStatusEnum",
        "type": "string"
      },
      "GateTypeEnum": {
        "enum": [
          "signature_blocks",
          "document_signed",
          "participant_approval",
          "file_upload",
          "countdown",
          "movement_authorized",
          "movement_reconciled",
          "information_request",
          "identity_verified"
        ],
        "title": "GateTypeEnum",
        "type": "string"
      },
      "InformationFieldOut": {
        "description": "One question on an information-request gate (set up in the Termn app).",
        "properties": {
          "key": {
            "description": "Stable key for this question; answers are keyed by it.",
            "title": "Key",
            "type": "string"
          },
          "label": {
            "title": "Label",
            "type": "string"
          },
          "type": {
            "description": "text, long_text, number, date, email, phone, choice, yes_no, or list.",
            "title": "Type",
            "type": "string"
          },
          "required": {
            "title": "Required",
            "type": "boolean"
          },
          "help": {
            "default": "",
            "title": "Help",
            "type": "string"
          },
          "options": {
            "default": [],
            "description": "The allowed answers for `choice` questions.",
            "items": {
              "type": "string"
            },
            "title": "Options",
            "type": "array"
          },
          "show_if": {
            "anyOf": [
              {
                "additionalProperties": true,
                "type": "object"
              },
              {
                "type": "null"
              }
            ],
            "description": "Present when the question is only asked after an earlier answer: {field, equals}.",
            "title": "Show If"
          }
        },
        "required": [
          "key",
          "label",
          "type",
          "required"
        ],
        "title": "InformationFieldOut",
        "type": "object"
      },
      "LifecycleEnum": {
        "enum": [
          "draft",
          "active",
          "completed",
          "cancelled",
          "expired"
        ],
        "title": "LifecycleEnum",
        "type": "string"
      },
      "Page_AgreementOut_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/AgreementOut"
            },
            "title": "Data",
            "type": "array"
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo"
          }
        },
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[AgreementOut]",
        "type": "object"
      },
      "ParticipantOut": {
        "properties": {
          "id": {
            "format": "uuid",
            "title": "Id",
            "type": "string"
          },
          "contact_id": {
            "format": "uuid",
            "title": "Contact Id",
            "type": "string"
          },
          "name": {
            "title": "Name",
            "type": "string"
          },
          "email": {
            "title": "Email",
            "type": "string"
          },
          "roles": {
            "items": {
              "type": "string"
            },
            "title": "Roles",
            "type": "array"
          }
        },
        "required": [
          "id",
          "contact_id",
          "name",
          "email",
          "roles"
        ],
        "title": "ParticipantOut",
        "type": "object"
      },
      "StageOut": {
        "properties": {
          "name": {
            "title": "Name",
            "type": "string"
          },
          "position": {
            "title": "Position",
            "type": "integer"
          },
          "status": {
            "$ref": "#/components/schemas/StageStatusEnum"
          },
          "gates": {
            "items": {
              "$ref": "#/components/schemas/GateOut"
            },
            "title": "Gates",
            "type": "array"
          }
        },
        "required": [
          "name",
          "position",
          "status",
          "gates"
        ],
        "title": "StageOut",
        "type": "object"
      },
      "StageStatusEnum": {
        "enum": [
          "pending",
          "active",
          "complete",
          "blocked"
        ],
        "title": "StageStatusEnum",
        "type": "string"
      },
      "AgreementIn": {
        "properties": {
          "workspace_id": {
            "description": "The workspace this agreement belongs to.",
            "format": "uuid",
            "title": "Workspace Id",
            "type": "string"
          },
          "name": {
            "maxLength": 250,
            "minLength": 1,
            "title": "Name",
            "type": "string"
          },
          "kind": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AgreementKindEnum"
              }
            ],
            "default": "general",
            "description": "`safe` applies SAFE-round presets and vocabulary; `general` fits everything else. When unsure, use `general`."
          },
          "summary": {
            "default": "",
            "description": "A short plain-language description. Deal terms, steps, and documents are set up in the Termn app after drafting.",
            "maxLength": 400,
            "minLength": 0,
            "title": "Summary",
            "type": "string"
          }
        },
        "required": [
          "workspace_id",
          "name"
        ],
        "title": "AgreementIn",
        "type": "object"
      },
      "InformationSubmissionOut": {
        "description": "One participant's answers to one information-request gate.\n\nOnly answers that have been sent appear here — a participant's unsent draft is\ntheirs alone. `accepted` means a member of the organization has read them.",
        "properties": {
          "gate_id": {
            "format": "uuid",
            "title": "Gate Id",
            "type": "string"
          },
          "gate_name": {
            "title": "Gate Name",
            "type": "string"
          },
          "participant_id": {
            "format": "uuid",
            "title": "Participant Id",
            "type": "string"
          },
          "participant_name": {
            "title": "Participant Name",
            "type": "string"
          },
          "status": {
            "description": "submitted, accepted, or changes_requested.",
            "title": "Status",
            "type": "string"
          },
          "answers": {
            "additionalProperties": true,
            "description": "Keyed by each question's `key`. A `list` question's answer is an array.",
            "title": "Answers",
            "type": "object"
          },
          "submitted_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Submitted At"
          },
          "reviewed_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Reviewed At"
          },
          "reviewer_note": {
            "default": "",
            "title": "Reviewer Note",
            "type": "string"
          }
        },
        "required": [
          "gate_id",
          "gate_name",
          "participant_id",
          "participant_name",
          "status",
          "answers"
        ],
        "title": "InformationSubmissionOut",
        "type": "object"
      },
      "ParticipantIn": {
        "properties": {
          "contact_id": {
            "description": "An existing contact in your organization.",
            "format": "uuid",
            "title": "Contact Id",
            "type": "string"
          },
          "roles": {
            "description": "What this participant does on the agreement.",
            "items": {
              "$ref": "#/components/schemas/ParticipantRoleEnum"
            },
            "title": "Roles",
            "type": "array"
          },
          "label": {
            "default": "",
            "maxLength": 120,
            "minLength": 0,
            "title": "Label",
            "type": "string"
          }
        },
        "required": [
          "contact_id",
          "roles"
        ],
        "title": "ParticipantIn",
        "type": "object"
      },
      "ParticipantRoleEnum": {
        "enum": [
          "signer",
          "funder",
          "receiver",
          "approver",
          "investor",
          "issuer",
          "trustee",
          "contributor",
          "observer"
        ],
        "title": "ParticipantRoleEnum",
        "type": "string"
      },
      "MovementOut": {
        "properties": {
          "id": {
            "format": "uuid",
            "title": "Id",
            "type": "string"
          },
          "agreement_id": {
            "format": "uuid",
            "title": "Agreement Id",
            "type": "string"
          },
          "purpose": {
            "title": "Purpose",
            "type": "string"
          },
          "asset_kind": {
            "description": "What moves: `funds`, `securities`, `real_property`, or `account_designation`. Only `funds` carries an amount.",
            "title": "Asset Kind",
            "type": "string"
          },
          "subject_display": {
            "description": "For non-funds kinds: what exactly moves, in plain words. Empty for funds.",
            "title": "Subject Display",
            "type": "string"
          },
          "amount_minor": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "description": "Amount in the currency's minor unit (cents for USD). `null` for non-funds kinds — the subject, not an amount, defines them.",
            "title": "Amount Minor"
          },
          "currency": {
            "title": "Currency",
            "type": "string"
          },
          "reference": {
            "title": "Reference",
            "type": "string"
          },
          "state": {
            "$ref": "#/components/schemas/MovementStateEnum",
            "description": "Machine state. Use this, not `state_sentence`, for logic."
          },
          "state_sentence": {
            "description": "Plain-language sentence for display. Never parse it.",
            "title": "State Sentence",
            "type": "string"
          },
          "sender": {
            "title": "Sender",
            "type": "string"
          },
          "receiver": {
            "title": "Receiver",
            "type": "string"
          },
          "reconciled": {
            "description": "True only when receiver-side evidence confirmed arrival. A sender's report never sets this.",
            "title": "Reconciled",
            "type": "boolean"
          },
          "confirmed_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO 8601. When receiver-side evidence confirmed arrival. `null` until reconciled.",
            "title": "Confirmed At"
          },
          "confirmed_by": {
            "description": "Who recorded the receiver-side confirmation. Empty until reconciled.",
            "title": "Confirmed By",
            "type": "string"
          },
          "confirmation_basis": {
            "description": "The evidence behind the confirmation, e.g. “Receiver confirmed with bank evidence — bank statement line, seen 2026-08-01”. Empty until reconciled.",
            "title": "Confirmation Basis",
            "type": "string"
          },
          "created_at": {
            "title": "Created At",
            "type": "string"
          },
          "updated_at": {
            "title": "Updated At",
            "type": "string"
          }
        },
        "required": [
          "id",
          "agreement_id",
          "purpose",
          "asset_kind",
          "subject_display",
          "amount_minor",
          "currency",
          "reference",
          "state",
          "state_sentence",
          "sender",
          "receiver",
          "reconciled",
          "confirmed_at",
          "confirmed_by",
          "confirmation_basis",
          "created_at",
          "updated_at"
        ],
        "title": "MovementOut",
        "type": "object"
      },
      "MovementStateEnum": {
        "enum": [
          "draft",
          "authorized",
          "instructions_ready",
          "sender_reported",
          "receipt_pending",
          "processing",
          "reconciled",
          "exception",
          "returned",
          "cancelled"
        ],
        "title": "MovementStateEnum",
        "type": "string"
      },
      "Page_MovementOut_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/MovementOut"
            },
            "title": "Data",
            "type": "array"
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo"
          }
        },
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[MovementOut]",
        "type": "object"
      },
      "LedgerRowOut": {
        "properties": {
          "key": {
            "description": "Stable row key, formatted `<type>:<uuid>`.",
            "title": "Key",
            "type": "string"
          },
          "title": {
            "title": "Title",
            "type": "string"
          },
          "product": {
            "title": "Product",
            "type": "string"
          },
          "kind": {
            "description": "`asset` when you are owed, `obligation` when you owe.",
            "title": "Kind",
            "type": "string"
          },
          "workspace_id": {
            "title": "Workspace Id",
            "type": "string"
          },
          "workspace_name": {
            "title": "Workspace Name",
            "type": "string"
          },
          "agreement_id": {
            "title": "Agreement Id",
            "type": "string"
          },
          "agreement_name": {
            "title": "Agreement Name",
            "type": "string"
          },
          "counterparty": {
            "title": "Counterparty",
            "type": "string"
          },
          "state_sentence": {
            "description": "Plain-language sentence for display. Never parse it.",
            "title": "State Sentence",
            "type": "string"
          },
          "phase": {
            "description": "One of `open`, `waiting`, `attention`, `done`.",
            "title": "Phase",
            "type": "string"
          },
          "amount_display": {
            "description": "Formatted for display, `—` when there is no amount.",
            "title": "Amount Display",
            "type": "string"
          },
          "amount_minor": {
            "description": "Amount in minor units. Zero when the row carries no amount.",
            "title": "Amount Minor",
            "type": "integer"
          },
          "currency": {
            "title": "Currency",
            "type": "string"
          },
          "due_on": {
            "anyOf": [
              {
                "format": "date",
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Due On"
          },
          "next_action": {
            "title": "Next Action",
            "type": "string"
          }
        },
        "required": [
          "key",
          "title",
          "product",
          "kind",
          "workspace_id",
          "workspace_name",
          "agreement_id",
          "agreement_name",
          "counterparty",
          "state_sentence",
          "phase",
          "amount_display",
          "amount_minor",
          "currency",
          "due_on",
          "next_action"
        ],
        "title": "LedgerRowOut",
        "type": "object"
      },
      "Page_LedgerRowOut_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/LedgerRowOut"
            },
            "title": "Data",
            "type": "array"
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo"
          }
        },
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[LedgerRowOut]",
        "type": "object"
      },
      "ProposalActionEnum": {
        "enum": [
          "publish_agreement",
          "cancel_agreement"
        ],
        "title": "ProposalActionEnum",
        "type": "string"
      },
      "ProposalOut": {
        "properties": {
          "id": {
            "format": "uuid",
            "title": "Id",
            "type": "string"
          },
          "action": {
            "$ref": "#/components/schemas/ProposalActionEnum"
          },
          "state": {
            "description": "`pending`, `confirmed`, `declined`, or `expired`.",
            "title": "State",
            "type": "string"
          },
          "agreement_id": {
            "format": "uuid",
            "title": "Agreement Id",
            "type": "string"
          },
          "summary": {
            "description": "Plain-language description of exactly what will happen.",
            "title": "Summary",
            "type": "string"
          },
          "confirm_url": {
            "description": "Where a member of your organization reviews and decides. Share this with a human; it requires their Termn login and cannot be actioned by API or MCP.",
            "title": "Confirm Url",
            "type": "string"
          },
          "expires_at": {
            "description": "ISO 8601. Undecided proposals expire at this instant.",
            "title": "Expires At",
            "type": "string"
          },
          "decided_by": {
            "description": "Who decided, once someone has.",
            "title": "Decided By",
            "type": "string"
          },
          "decided_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Decided At"
          },
          "decision_note": {
            "title": "Decision Note",
            "type": "string"
          },
          "created_at": {
            "title": "Created At",
            "type": "string"
          }
        },
        "required": [
          "id",
          "action",
          "state",
          "agreement_id",
          "summary",
          "confirm_url",
          "expires_at",
          "decided_by",
          "decided_at",
          "decision_note",
          "created_at"
        ],
        "title": "ProposalOut",
        "type": "object"
      },
      "ProposalIn": {
        "properties": {
          "action": {
            "$ref": "#/components/schemas/ProposalActionEnum",
            "description": "`publish_agreement` sends the agreement to its participants once a member confirms. `cancel_agreement` stops it and disables every participant link."
          },
          "agreement_id": {
            "format": "uuid",
            "title": "Agreement Id",
            "type": "string"
          },
          "note": {
            "default": "",
            "description": "Optional context shown to the member who decides, e.g. why you propose this.",
            "maxLength": 300,
            "minLength": 0,
            "title": "Note",
            "type": "string"
          }
        },
        "required": [
          "action",
          "agreement_id"
        ],
        "title": "ProposalIn",
        "type": "object"
      },
      "Page_ProposalOut_": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/ProposalOut"
            },
            "title": "Data",
            "type": "array"
          },
          "pagination": {
            "$ref": "#/components/schemas/PageInfo"
          }
        },
        "required": [
          "data",
          "pagination"
        ],
        "title": "Page[ProposalOut]",
        "type": "object"
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "status",
              "request_id"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable code. Branch on this.",
                "enum": [
                  "not_found",
                  "unauthenticated",
                  "forbidden",
                  "invalid_request",
                  "method_not_allowed",
                  "conflict",
                  "internal_error"
                ]
              },
              "message": {
                "type": "string",
                "description": "Human-readable explanation."
              },
              "status": {
                "type": "integer"
              },
              "request_id": {
                "type": "string",
                "description": "Quote this when contacting support."
              },
              "errors": {
                "type": "array",
                "description": "Present on validation failures.",
                "items": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "servers": [
    {
      "url": "https://termn.ai",
      "description": "Termn"
    }
  ],
  "tags": [
    {
      "name": "Identity",
      "description": "Who the calling token belongs to."
    },
    {
      "name": "Contacts",
      "description": "People and entities your organization does business with. Organization-scoped and reusable; agreements reference contacts rather than copying them."
    },
    {
      "name": "Workspaces",
      "description": "One workspace per deal or matter. Agreements live inside a workspace."
    },
    {
      "name": "Agreements",
      "description": "Agreements with their ordered stages and gates. Create drafts here; publishing an agreement to participants happens in the Termn app."
    },
    {
      "name": "Money movement",
      "description": "**Read-only, deliberately.** Authorizing a payment and confirming that one arrived are evidence-gated acts: only receiver-side evidence at recipient-confirmed assurance or better may reconcile a movement, and a sender's acknowledgement never proves receipt. An organization-wide API token is not that evidence, so those transitions are performed by a person in the Termn app and observed here."
    },
    {
      "name": "Ledger",
      "description": "Every obligation and asset across all workspaces, in one flat list."
    },
    {
      "name": "Proposals",
      "description": "The only door to protected effects. A machine credential may *propose* publishing or cancelling an agreement; a member of the organization reviews the plain-language summary at `confirm_url` in the Termn app and confirms or declines it there. Nothing executes until they do. Undecided proposals expire after 72 hours."
    }
  ]
}
