openapi: 3.1.0
info:
  title: Thaw API
  version: '1'
  summary: Your documents, the people & things they are about, and the Checkup, as JSON.
  description: |
    Every call carries a bearer token and answers as JSON. A personal token
    is made in Settings > Developers and belongs to the person, not to one
    family or business: the `Thaw-Account` header names which one a call is
    for, and without it the token opens the first one the person belongs to.
    An assistant connection (OAuth, the same one the MCP server issues) is
    bound to one family or business and needs no header. Thaw's own app
    holds a token of its own, and a few paths are the app's alone (marked
    `x-audience: app` in the source file): they are left out of the
    published reference and answer 404 to any other token, the same 404 an
    unrouted address gets.

    What a token may do is bounded three ways, and the narrowest wins: the
    scopes on the token, the person's role in that family or business, and
    the plan. A viewer's token reads whatever its scopes say. Account, policy
    and license numbers come back only with the `identifiers` scope on a
    token, or on a read-and-change connection.

    Every refusal is `application/problem+json` (RFC 9457) with a `status`,
    a `title` and a `detail` sentence a person could read out. A missing
    scope is 403 with `scope` naming the one needed and a `WWW-Authenticate`
    header saying `insufficient_scope`. Calls are paced per token and per
    account; past the pace the answer is 429 with `Retry-After`.

    Changing things needs the `write` scope, an owner or member, and a
    paid plan; every change is the same call the page and the MCP tools
    make, and is audited as made through the API. A change call may carry
    an `Idempotency-Key` header: the same key with the same body within a
    day replays the first answer (marked `Idempotent-Replayed: true`), the
    same key with a different body is 422, and the same key while the
    first call is still running is 409.
externalDocs:
  description: The reference, with guides
  url: https://developer.thawed.app
servers:
  - url: https://api.thawed.app/v1
    description: >-
      Production. The OAuth documents sit beside it at /.well-known/oauth-protected-resource and
      /.well-known/oauth-authorization-server on the same host.
security:
  - bearer: []
tags:
  - name: You
    description: Who the token is, and where it may act.
  - name: Documents
    description: >-
      What is on file: listed, searched, opened with a window of its words, added, filed as something else, moved,
      linked to people and things, shared out. A change needs the `write` scope; adding one needs the role and the scope
      but not the plan.
  - name: Facts
    description: >-
      What Thaw took off the pages, with sums. Account, policy and license numbers come only with the `identifiers`
      scope.
  - name: People & things
    description: >-
      Who and what the documents are about: people, pets, vehicles, homes, accounts, policies, companies. Renamed,
      combined and answered here as on the page.
  - name: Checkup
    description: >-
      Everything that needs a person, in one shape: the dated items and what is missing, each handled, snoozed or
      dismissed here as on the page.
  - name: Bundles
    description: A named set of documents, made by hand, and the year-end tax package Thaw assembles from a rule.
  - name: Imports
    description: A previous system's export, a cloud folder or a mailbox brought in, with where each stands.
  - name: Uploads
    description: >-
      A large file in parts, straight to storage, the way the browser sends anything past the plain upload limit and
      every archive.
  - name: Ask
    description: >-
      Thaw's answer to a question, streamed as server-sent events, and the email drafts. Each one counts as an ask on
      the plan. Needs the `ask` scope.
paths:
  /me:
    get:
      tags:
        - You
      summary: Who this token is
      description: >-
        Start here. The person behind the token, the family or business this call landed in (the `Thaw-Account` header,
        else their first), the token's scopes and what it may do here once the role and the plan have had their say, and
        every family or business the token could name instead.
      operationId: me
      responses:
        '200':
          description: >-
            The person, the family or business the call landed in, the token and what it may do here, and every family
            or business the token could name in the header.
          content:
            application/json:
              schema:
                type: object
                properties:
                  user:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type:
                          - string
                          - 'null'
                      email:
                        type: string
                  account:
                    type: object
                    properties:
                      id:
                        type: string
                      name:
                        type: string
                      kind:
                        type: string
                        enum:
                          - family
                          - business
                      role:
                        type: string
                        enum:
                          - owner
                          - member
                          - viewer
                          - view
                          - edit
                          - upload_only
                      bundle:
                        type:
                          - string
                          - 'null'
                        description: The one bundle a guest was let in on, when that is all they may see.
                      family_or_business:
                        type: object
                        properties:
                          id:
                            type: string
                          name:
                            type: string
                          kind:
                            type: string
                      plan:
                        type: object
                        properties:
                          name:
                            type: string
                          label:
                            type: string
                  token:
                    type: object
                    description: >-
                      A personal or app token (kind, name, label, scopes, expires_at) or a connection (kind
                      "connection", the app's name, scopes).
                  can:
                    type: object
                    properties:
                      change:
                        type: boolean
                        description: Whether a change would be allowed here (the role, the plan and the token all agreeing).
                      identifiers:
                        type: boolean
                        description: Whether account, policy and license numbers come back.
                  accounts:
                    type: array
                    description: >-
                      Every family or business a personal token may name in the Thaw-Account header; empty for a
                      connection.
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                        name:
                          type: string
                        kind:
                          type: string
                        role:
                          type: string
                        guest:
                          type: boolean
                  header:
                    type: string
                    const: Thaw-Account
                  api:
                    type: string
                    description: The versioned base every URL in the answers hangs off.
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
  /overview:
    get:
      tags:
        - You
      summary: What is on file, in numbers
      description: >-
        The family or business in one look - documents by category and by type, the people & things by kind, the
        Checkup's counts, the plan's room - and the URLs of the four places.
      operationId: overview
      responses:
        '200':
          description: The overview.
          content:
            application/json:
              schema:
                type: object
                properties:
                  family_or_business:
                    type: object
                    properties:
                      name:
                        type: string
                      kind:
                        type: string
                  you:
                    type: object
                    description: Your name and role, and the token or connection the call came on.
                  documents:
                    type: object
                    properties:
                      current:
                        type: integer
                      including_replaced:
                        type: integer
                      needs_a_look:
                        type: integer
                      being_read:
                        type: integer
                      arrived_last_7_days:
                        type: integer
                      years:
                        type: object
                  categories:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        slug:
                          type: string
                        documents:
                          type: integer
                  types:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        slug:
                          type: string
                        documents:
                          type: integer
                  people_and_things:
                    type: array
                    items:
                      type: object
                      properties:
                        kind:
                          type: string
                        label:
                          type: string
                        count:
                          type: integer
                  checkup:
                    type: object
                    properties:
                      open:
                        type: integer
                      past_due_or_urgent:
                        type: integer
                      snoozed:
                        type: integer
                      handled:
                        type: integer
                  plan:
                    type: object
                    properties:
                      label:
                        type: string
                      questions_left:
                        type:
                          - integer
                          - 'null'
                      full_reads_left:
                        type:
                          - integer
                          - 'null'
                  ways_in:
                    type: string
                  urls:
                    type: object
        '401':
          $ref: '#/components/responses/NotSignedIn'
  /search:
    get:
      tags:
        - Documents
      summary: Search
      description: >-
        The Library's search - words, meaning, an identifier typed in, and the people & things by name, fused - with the
        Library's filters on top. An identifier that matched is named in `matched_on`; the number itself comes back only
        with the `identifiers` scope.
      operationId: search
      parameters:
        - name: q
          in: query
          required: true
          schema:
            type: string
            maxLength: 200
        - $ref: '#/components/parameters/category'
        - $ref: '#/components/parameters/type'
        - $ref: '#/components/parameters/personOrThing'
        - $ref: '#/components/parameters/year'
        - $ref: '#/components/parameters/includeSuperseded'
        - name: meaning
          in: query
          schema:
            type: boolean
            default: true
          description: Search by meaning as well as by words. Off is faster.
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 50
            default: 20
      responses:
        '200':
          description: The documents found, best first, and the people & things whose names matched.
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      allOf:
                        - $ref: '#/components/schemas/DocumentRow'
                        - type: object
                          properties:
                            snippet:
                              type:
                                - string
                                - 'null'
                            matched_on:
                              type:
                                - string
                                - 'null'
                  total:
                    type: integer
                  people_and_things:
                    type: array
                    items:
                      $ref: '#/components/schemas/Named'
                  library_url:
                    type: string
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '422':
          $ref: '#/components/responses/Invalid'
  /documents:
    get:
      tags:
        - Documents
      summary: The documents
      description: >-
        The Library's filters, newest first by the date on the document (the first known of the printed date, the
        period, the day it was had before Thaw, the arrival) or by arrival. Pages by cursor; a cursor stays right when
        documents arrive between pages.
      operationId: listDocuments
      parameters:
        - $ref: '#/components/parameters/category'
        - $ref: '#/components/parameters/type'
        - $ref: '#/components/parameters/personOrThing'
        - $ref: '#/components/parameters/year'
        - name: since
          in: query
          schema:
            type: string
            format: date
          description: Arrived on or after this day.
        - name: needs_look
          in: query
          schema:
            type: boolean
          description: Only the ones waiting on a person.
        - name: shred
          in: query
          schema:
            type: boolean
          description: Only the ones past their keep-until date.
        - $ref: '#/components/parameters/includeSuperseded'
        - name: sort
          in: query
          schema:
            type: string
            enum:
              - date
              - arrival
            default: date
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 50
        - name: cursor
          in: query
          schema:
            type: string
          description: The `next_cursor` of the page before.
      responses:
        '200':
          description: One page of documents.
          content:
            application/json:
              schema:
                type: object
                properties:
                  documents:
                    type: array
                    items:
                      $ref: '#/components/schemas/DocumentRow'
                  total:
                    type: integer
                    description: How many match the filters altogether.
                  next_cursor:
                    type:
                      - string
                      - 'null'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '422':
          $ref: '#/components/responses/Invalid'
    post:
      x-scope: add
      tags:
        - Documents
      summary: A document in
      description: |
        One file as `file`, or photographs of pages as `pages[]`, assembled
        into one PDF the way the scan page assembles them (`separate` makes
        one document per page; one page alone is one document). In a
        business, `for=receipt` tells Thaw what it is looking at, and
        several pages are always one receipt. Read in full, live. Refused
        with 403 when the plan has no room. Sent as multipart form data.
      operationId: addDocument
      parameters:
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                pages:
                  type: array
                  items:
                    type: string
                    format: binary
                  maxItems: 30
                  description: JPEG, PNG, WebP, HEIC or PDF, one per page.
                title:
                  type: string
                  maxLength: 120
                separate:
                  type: boolean
                for:
                  type: string
                  enum:
                    - receipt
      responses:
        '201':
          description: Received. Thaw is reading it.
          content:
            application/json:
              schema:
                type: object
                properties:
                  documents:
                    type: array
                    items:
                      $ref: '#/components/schemas/DocumentRow'
                  message:
                    type: string
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '422':
          $ref: '#/components/responses/Invalid'
  /documents/{document}:
    get:
      tags:
        - Documents
      summary: One document
      description: >-
        The document in full - the row, the summary, the facts, what is due, what it replaced, the links out - and, with
        `text=1`, a window of its words by page. Asking for the words is a view of the document and is written to the
        audit log as one.
      operationId: getDocument
      parameters:
        - $ref: '#/components/parameters/document'
        - name: text
          in: query
          schema:
            type: boolean
            default: false
        - name: page
          in: query
          schema:
            type: integer
            minimum: 1
          description: The page the window starts on.
        - name: max_chars
          in: query
          schema:
            type: integer
            minimum: 500
            maximum: 60000
            default: 12000
      responses:
        '200':
          description: The document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Document'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      x-scope: write
      tags:
        - Documents
      summary: Correct a document
      description: >-
        The review loop - title, summary, the date printed on it, the type (by id, slug or name, or null to clear it)
        and the category. Whatever is corrected is the person's from then on, and a later re-read never touches it. At
        least one field.
      operationId: updateDocument
      parameters:
        - $ref: '#/components/parameters/document'
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  maxLength: 140
                summary:
                  type:
                    - string
                    - 'null'
                  maxLength: 2000
                doc_date:
                  type:
                    - string
                    - 'null'
                  format: date
                type:
                  type:
                    - string
                    - 'null'
                  maxLength: 120
                category_id:
                  type:
                    - string
                    - 'null'
                  format: uuid
      responses:
        '200':
          description: The document, and which fields changed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  document:
                    $ref: '#/components/schemas/DocumentRow'
                  edited:
                    type: array
                    items:
                      type: string
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Invalid'
  /documents/{document}/facts:
    get:
      tags:
        - Facts
      summary: A document's facts
      description: >-
        Every fact Thaw took off the page, with its role. Account, policy and license numbers come only on a token with
        the `identifiers` scope; without it they are left out rather than masked.
      operationId: documentFacts
      parameters:
        - $ref: '#/components/parameters/document'
      responses:
        '200':
          description: The facts, identifiers left out without the scope.
          content:
            application/json:
              schema:
                type: object
                properties:
                  document:
                    $ref: '#/components/schemas/Named'
                  facts:
                    type: array
                    items:
                      $ref: '#/components/schemas/Fact'
                  identifiers_shown:
                    type: boolean
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '404':
          $ref: '#/components/responses/NotFound'
  /documents/{document}/files/{file}:
    get:
      x-scope: files
      tags:
        - Documents
      summary: The file itself
      description: >-
        Streamed as the web streams it, decrypted on the way out. Needs the `files` scope. Never a quarantined file.
        `download=1` sends it as an attachment.
      operationId: documentFile
      parameters:
        - $ref: '#/components/parameters/document'
        - $ref: '#/components/parameters/file'
        - name: download
          in: query
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: The file, with its own content type.
          content:
            '*/*':
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '410':
          $ref: '#/components/responses/Problem'
  /documents/{document}/files/{file}/pages/{page}:
    get:
      x-scope: files
      tags:
        - Documents
      summary: One page as a picture
      description: A PDF's page drawn as a PNG, as the phone shows it. Needs the `files` scope. Up to 2200 px on the long edge.
      operationId: documentPage
      parameters:
        - $ref: '#/components/parameters/document'
        - $ref: '#/components/parameters/file'
        - name: page
          in: path
          required: true
          schema:
            type: integer
            minimum: 1
        - name: w
          in: query
          schema:
            type: integer
            minimum: 600
            maximum: 2200
            default: 1600
          description: The long edge in pixels.
      responses:
        '200':
          description: The page.
          content:
            image/png:
              schema:
                type: string
                format: binary
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
  /facts:
    get:
      tags:
        - Facts
      summary: Facts across documents
      description: >-
        Every fact on the documents the filters pick, with the sum of the money ones by currency. `role=identifier`
        needs the `identifiers` scope on a token, or a read-and-change connection.
      operationId: listFacts
      parameters:
        - name: label
          in: query
          schema:
            type: string
            maxLength: 80
          description: Part of the fact's label or key ("premium", "due date").
        - name: role
          in: query
          schema:
            type: string
            enum:
              - money
              - date
              - party
              - text
              - identifier
        - $ref: '#/components/parameters/category'
        - $ref: '#/components/parameters/type'
        - $ref: '#/components/parameters/personOrThing'
        - $ref: '#/components/parameters/year'
        - $ref: '#/components/parameters/includeSuperseded'
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 100
      responses:
        '200':
          description: The facts, newest document first.
          content:
            application/json:
              schema:
                type: object
                properties:
                  facts:
                    type: array
                    items:
                      allOf:
                        - $ref: '#/components/schemas/Fact'
                        - type: object
                          properties:
                            document:
                              $ref: '#/components/schemas/Named'
                  showing:
                    type: integer
                  total:
                    type: integer
                  sum_of_amounts:
                    type: object
                    additionalProperties:
                      type: number
                    description: By currency, over every matching money fact, not only the ones shown.
                  identifiers_shown:
                    type: boolean
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '422':
          $ref: '#/components/responses/Invalid'
  /people-and-things:
    get:
      tags:
        - People & things
      summary: Everyone and everything
      description: >-
        By kind, each with how much paper it is on and the one status Home shows. VINs, plates and the like are details
        that come back only with the `identifiers` scope.
      operationId: listPeopleAndThings
      parameters:
        - name: kind
          in: query
          schema:
            type: string
            enum:
              - person
              - pet
              - vehicle
              - property
              - organization
              - account
              - policy
              - item
        - name: include_gone
          in: query
          schema:
            type: boolean
            default: false
          description: Include what was sold, ended or said to be not yours.
      responses:
        '200':
          description: The list.
          content:
            application/json:
              schema:
                type: object
                properties:
                  people_and_things:
                    type: array
                    items:
                      allOf:
                        - $ref: '#/components/schemas/PersonOrThing'
                        - type: object
                          properties:
                            documents:
                              type: integer
                            status:
                              $ref: '#/components/schemas/Status'
                  total:
                    type: integer
                  url:
                    type: string
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '422':
          $ref: '#/components/responses/Invalid'
  /people-and-things/{entity}:
    get:
      tags:
        - People & things
      summary: One person or thing
      description: The current facts about it, its documents, what is due and what is missing.
      operationId: getPersonOrThing
      parameters:
        - $ref: '#/components/parameters/entity'
      responses:
        '200':
          description: The person or thing in full.
          content:
            application/json:
              schema:
                type: object
                properties:
                  person_or_thing:
                    $ref: '#/components/schemas/PersonOrThing'
                  status:
                    $ref: '#/components/schemas/Status'
                  facts:
                    type: array
                    items:
                      $ref: '#/components/schemas/Fact'
                  identifiers_shown:
                    type: boolean
                  documents:
                    type: array
                    items:
                      $ref: '#/components/schemas/DocumentRow'
                  up_next:
                    type: array
                    items:
                      $ref: '#/components/schemas/UpNext'
                  missing:
                    type: array
                    items:
                      type: object
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      x-scope: write
      tags:
        - People & things
      summary: Correct a person or thing
      description: >-
        The name, the kind, what is known about it (`attributes`, merged; a null value removes one), when it was sold or
        ended (`disposed_at`), whether it is pinned, and whether it is part of the household (`household`, which is what
        the setup cards ask). At least one field. Something disposed of has its open items closed.
      operationId: updatePersonOrThing
      parameters:
        - $ref: '#/components/parameters/entity'
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  maxLength: 120
                kind:
                  type: string
                  enum:
                    - person
                    - pet
                    - organization
                    - property
                    - vehicle
                    - financial_account
                    - policy
                    - asset
                attributes:
                  type: object
                  additionalProperties:
                    type:
                      - string
                      - 'null'
                    maxLength: 200
                disposed_at:
                  type:
                    - string
                    - 'null'
                  format: date
                pinned:
                  type: boolean
                household:
                  type:
                    - boolean
                    - 'null'
      responses:
        '200':
          description: As it stands now.
          content:
            application/json:
              schema:
                type: object
                properties:
                  person_or_thing:
                    $ref: '#/components/schemas/PersonOrThing'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Invalid'
  /checkup:
    get:
      tags:
        - Checkup
      summary: The Checkup
      description: >-
        One list in the app's order - past due, this month and the next, missing, housekeeping, snoozed - narrowed by
        group, by a window of dates, by person or thing, or shown as handled or dismissed.
      operationId: checkup
      parameters:
        - name: group
          in: query
          schema:
            type: string
          description: '`past_due`, `missing`, `housekeeping`, `snoozed`, `dated` (anything with a date), or a month as `YYYY-MM`.'
        - name: from
          in: query
          schema:
            type: string
            format: date
        - name: to
          in: query
          schema:
            type: string
            format: date
        - $ref: '#/components/parameters/personOrThing'
        - name: show
          in: query
          schema:
            type: string
            enum:
              - open
              - handled
              - dismissed
            default: open
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 200
            default: 100
      responses:
        '200':
          description: The items.
          content:
            application/json:
              schema:
                type: object
                properties:
                  items:
                    type: array
                    items:
                      $ref: '#/components/schemas/CheckupItem'
                  total:
                    type: integer
                  by_group:
                    type: object
                    additionalProperties:
                      type: integer
                  counts:
                    type: object
                    additionalProperties:
                      type: integer
                  url:
                    type: string
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Invalid'
  /bundles:
    get:
      tags:
        - Bundles
      summary: The bundles
      description: Named sets of documents, the year-end package among them.
      operationId: listBundles
      responses:
        '200':
          description: The list.
          content:
            application/json:
              schema:
                type: object
                properties:
                  bundles:
                    type: array
                    items:
                      type: object
        '401':
          $ref: '#/components/responses/NotSignedIn'
  /bundles/{bundle}:
    get:
      tags:
        - Bundles
      summary: One bundle
      description: >-
        One bundle by id, with its documents. A hand-made bundle is a person's; an assembled one (the tax package) is
        Thaw's, rebuilt from its rule, and refuses changes by hand.
      operationId: getBundle
      parameters:
        - $ref: '#/components/parameters/bundle'
      responses:
        '200':
          description: The bundle and its documents, as its page has them.
          content:
            application/json:
              schema:
                type: object
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '404':
          $ref: '#/components/responses/NotFound'
  /imports:
    get:
      tags:
        - Imports
      summary: The imports
      description: >-
        Every import this family or business has made, newest first, each with one phrase for where it stands ("Sorting
        · 2,900 of 3,104").
      operationId: listImports
      responses:
        '200':
          description: Every import, newest first, each with the one phrase the imports list shows.
          content:
            application/json:
              schema:
                type: object
                properties:
                  imports:
                    type: array
                    items:
                      type: object
        '401':
          $ref: '#/components/responses/NotSignedIn'
  /imports/{import}:
    get:
      tags:
        - Imports
      summary: One import
      description: One import by id, with the counts per stage and what was left behind when the plan's room ran out.
      operationId: getImport
      parameters:
        - $ref: '#/components/parameters/import'
      responses:
        '200':
          description: How the import is getting on, as its page says it.
          content:
            application/json:
              schema:
                type: object
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '404':
          $ref: '#/components/responses/NotFound'
  /calendar.ics:
    get:
      tags:
        - Checkup
      summary: Up next as a calendar
      description: The dated items as an iCalendar feed, the same one Settings > Calendar hands out.
      operationId: calendar
      responses:
        '200':
          description: The feed.
          content:
            text/calendar:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/NotSignedIn'
  /ask:
    post:
      x-scope: ask
      tags:
        - Ask
      summary: Ask a question
      description: |
        Thaw's answer, grounded in the documents and the Checkup's dated
        items, streamed as server-sent events: `text` events carry the
        answer as it is written, then one `done` event carries the whole
        turn (the answer, its citations, whether it was grounded, any change
        it proposes), or one `error` event says what went wrong. The thread
        remembers the last few turns per person. Needs the `ask` scope; on
        Free the answer is the plan's ten questions.
      operationId: ask
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - question
              properties:
                question:
                  type: string
                  minLength: 3
                  maxLength: 600
      responses:
        '200':
          description: The stream.
          content:
            text/event-stream:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '422':
          $ref: '#/components/responses/Invalid'
  /documents/{document}/ask:
    post:
      x-scope: ask
      tags:
        - Ask
      summary: Ask about one document
      description: >-
        The same stream, answered from the document's own pages and items, so every citation is a page of it. `history`
        is the last few turns about this document, kept on the device.
      operationId: askDocument
      parameters:
        - $ref: '#/components/parameters/document'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - question
              properties:
                question:
                  type: string
                  minLength: 3
                  maxLength: 600
                history:
                  type: array
                  maxItems: 4
                  items:
                    type: object
                    required:
                      - question
                      - answer
                    properties:
                      question:
                        type: string
                      answer:
                        type: string
      responses:
        '200':
          description: The stream.
          content:
            text/event-stream:
              schema:
                type: string
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Invalid'
  /checkup/obligations/{obligation}/draft:
    post:
      x-scope: ask
      tags:
        - Ask
      summary: Draft the email for a dated item
      description: >-
        The email a person would send about this item, written from what is on file, with account and policy numbers
        filled in only where the token may see them and `[claim number]` to fill by hand otherwise. Comes with the paid
        plans.
      operationId: draftForObligation
      parameters:
        - name: obligation
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                about:
                  type: string
                  maxLength: 240
                  description: What the email should ask for, in the person's words.
      responses:
        '200':
          $ref: '#/components/responses/Draft'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/Problem'
  /checkup/{gap}/draft:
    post:
      x-scope: ask
      tags:
        - Ask
      summary: Draft the email for something missing
      description: >-
        The email to send about something missing from the Checkup, written from what is on file, with placeholders
        where a number would go. `about` is what to ask for in the person's words, when the gap's own words are not it.
      operationId: draftForGap
      parameters:
        - name: gap
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                about:
                  type: string
                  maxLength: 240
      responses:
        '200':
          $ref: '#/components/responses/Draft'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '503':
          $ref: '#/components/responses/Problem'
  /documents/note:
    post:
      x-scope: add
      tags:
        - Documents
      summary: Save a note as a document
      description: >-
        Words become a page, filed and read like anything else - what the MCP `save_note` tool does. `person_or_thing`
        (an id or a name) links it; `date` is the day it is about, today if none.
      operationId: saveNote
      parameters:
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - title
                - text
              properties:
                title:
                  type: string
                  maxLength: 120
                text:
                  type: string
                  maxLength: 20000
                person_or_thing:
                  type: string
                  maxLength: 120
                date:
                  type: string
                  format: date
      responses:
        '201':
          description: The document.
          content:
            application/json:
              schema:
                type: object
                properties:
                  document:
                    $ref: '#/components/schemas/DocumentRow'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '422':
          $ref: '#/components/responses/Invalid'
  /uploads:
    post:
      x-scope: add
      tags:
        - Uploads
      summary: Start an upload in parts
      description: >-
        For a file past the plain limit, or any archive. Answers with the key, the upload id, the size of a part and how
        many parts the file takes; then `/uploads/urls` hands out a URL per part to PUT to, and `/uploads/complete`
        makes the document (or the import, for an archive). The family or business that started an upload is the only
        one that can finish it.
      operationId: startUpload
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - size
              properties:
                name:
                  type: string
                  maxLength: 255
                size:
                  type: integer
                  minimum: 1
                mime:
                  type: string
                  maxLength: 120
      responses:
        '200':
          description: Where to put the parts.
          content:
            application/json:
              schema:
                type: object
                properties:
                  key:
                    type: string
                  upload_id:
                    type: string
                  part_bytes:
                    type: integer
                  parts:
                    type: integer
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '422':
          $ref: '#/components/responses/Invalid'
  /uploads/urls:
    post:
      x-scope: add
      tags:
        - Uploads
      summary: URLs for the next parts
      description: >-
        Up to twenty part numbers at a time; each URL takes one PUT of that part's bytes and answers with an ETag to
        keep.
      operationId: uploadPartUrls
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - key
                - upload_id
                - parts
              properties:
                key:
                  type: string
                upload_id:
                  type: string
                parts:
                  type: array
                  items:
                    type: integer
                    minimum: 1
                    maximum: 10000
                  maxItems: 20
      responses:
        '200':
          description: One URL per part, keyed by part number.
          content:
            application/json:
              schema:
                type: object
                properties:
                  urls:
                    type: object
                    additionalProperties:
                      type: string
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '422':
          $ref: '#/components/responses/Invalid'
  /uploads/complete:
    post:
      x-scope: add
      tags:
        - Uploads
      summary: Finish an upload
      description: >-
        Every part's number and ETag. A document is copied in, sealed and read; a `.zip` or `.enex` becomes an import,
        and the answer names it.
      operationId: completeUpload
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - key
                - upload_id
                - name
                - parts
              properties:
                key:
                  type: string
                upload_id:
                  type: string
                name:
                  type: string
                  maxLength: 255
                mime:
                  type: string
                  maxLength: 120
                parts:
                  type: array
                  items:
                    type: object
                    required:
                      - number
                      - etag
                    properties:
                      number:
                        type: integer
                      etag:
                        type: string
      responses:
        '200':
          description: Done. `import_id` is set when the file was an archive.
          content:
            application/json:
              schema:
                type: object
                properties:
                  import_id:
                    type:
                      - string
                      - 'null'
                  redirect:
                    type:
                      - string
                      - 'null'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '422':
          $ref: '#/components/responses/Invalid'
  /uploads/abort:
    post:
      x-scope: add
      tags:
        - Uploads
      summary: Abandon an upload
      description: >-
        Give up on an upload started with `POST /uploads`: the parts sent so far are discarded and nothing is filed.
        Only the family or business that started it can abandon it.
      operationId: abortUpload
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - key
                - upload_id
              properties:
                key:
                  type: string
                upload_id:
                  type: string
      responses:
        '200':
          description: Done.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '422':
          $ref: '#/components/responses/Invalid'
  /documents/{document}/type:
    post:
      x-scope: write
      tags:
        - Documents
      summary: File a document as something else
      description: >-
        An existing type by `type_id`, or a new one of the family's own by `name` and `category_id`. `move_all` also
        moves everything filed as the type this document carries now, which is how a household merges two of its own
        types.
      operationId: fileDocumentAs
      parameters:
        - $ref: '#/components/parameters/document'
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                type_id:
                  type: string
                  format: uuid
                name:
                  type: string
                  maxLength: 80
                category_id:
                  type: string
                  format: uuid
                move_all:
                  type: boolean
      responses:
        '200':
          description: The document, the type it carries now, and how many others moved with it.
          content:
            application/json:
              schema:
                type: object
                properties:
                  document:
                    $ref: '#/components/schemas/DocumentRow'
                  type:
                    $ref: '#/components/schemas/Named'
                  moved:
                    type: integer
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Invalid'
  /documents/{document}/entities:
    post:
      x-scope: write
      tags:
        - Documents
      summary: Link a person or thing
      description: >-
        One already on file by `entity_id`, or a new one by `name` and `kind`. `role` is what the document is to them -
        `subject` unless said otherwise.
      operationId: linkPersonOrThing
      parameters:
        - $ref: '#/components/parameters/document'
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                entity_id:
                  type: string
                  format: uuid
                name:
                  type: string
                  maxLength: 120
                kind:
                  type: string
                  enum:
                    - person
                    - pet
                    - organization
                    - property
                    - vehicle
                    - financial_account
                    - policy
                    - asset
                role:
                  type: string
                  enum:
                    - subject
                    - issuer
                    - payer
                    - payee
                    - owner
                    - covered
      responses:
        '200':
          description: The person or thing, and the document.
          content:
            application/json:
              schema:
                type: object
                properties:
                  person_or_thing:
                    $ref: '#/components/schemas/PersonOrThing'
                  document:
                    $ref: '#/components/schemas/DocumentRow'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Invalid'
  /documents/{document}/entities/{entity}:
    delete:
      x-scope: write
      tags:
        - Documents
      summary: Unlink a person or thing
      description: >-
        Take a person or thing off a document. The link goes, the document and the person stay; a person left on no
        document at all is tidied away by the next recompute.
      operationId: unlinkPersonOrThing
      parameters:
        - $ref: '#/components/parameters/document'
        - $ref: '#/components/parameters/entity'
      responses:
        '200':
          description: The document.
          content:
            application/json:
              schema:
                type: object
                properties:
                  document:
                    $ref: '#/components/schemas/DocumentRow'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
  /documents/{document}/facts/{fact}:
    patch:
      x-scope: write
      tags:
        - Facts
      summary: Correct a fact
      description: >-
        The value is the person's from then on. A number is read from it where the fact is money, a date where it is a
        date.
      operationId: correctFact
      parameters:
        - $ref: '#/components/parameters/document'
        - name: fact
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - value
              properties:
                value:
                  type: string
                  maxLength: 500
      responses:
        '200':
          description: The fact, and the document.
          content:
            application/json:
              schema:
                type: object
                properties:
                  fact:
                    $ref: '#/components/schemas/Fact'
                  document:
                    $ref: '#/components/schemas/DocumentRow'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Invalid'
  /documents/{document}/reviewed:
    post:
      x-scope: write
      tags:
        - Documents
      summary: Looks right
      description: A document waiting on a look is filed as it stands.
      operationId: markReviewed
      parameters:
        - $ref: '#/components/parameters/document'
      responses:
        '200':
          description: The document.
          content:
            application/json:
              schema:
                type: object
                properties:
                  document:
                    $ref: '#/components/schemas/DocumentRow'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
  /documents/{document}/read-again:
    post:
      x-scope: write
      tags:
        - Documents
      summary: Read it again
      description: A fresh read in full, live. A file that failed the virus scan stays closed.
      operationId: readAgain
      parameters:
        - $ref: '#/components/parameters/document'
      responses:
        '202':
          description: Reading. The document, as it stands before the read.
          content:
            application/json:
              schema:
                type: object
                properties:
                  document:
                    $ref: '#/components/schemas/DocumentRow'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
  /documents/{document}/links:
    post:
      x-scope: write
      tags:
        - Documents
      summary: Share a document by link
      description: >-
        A link anyone holding can open, with a life in days, a passcode if wanted and a view limit. The URL is in this
        answer and nowhere else.
      operationId: shareDocument
      parameters:
        - $ref: '#/components/parameters/document'
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - days
              properties:
                days:
                  type: integer
                  minimum: 1
                  maximum: 30
                passcode:
                  type: string
                  minLength: 4
                  maxLength: 64
                max_views:
                  type: integer
                  minimum: 1
                  maximum: 1000
      responses:
        '201':
          description: The link.
          content:
            application/json:
              schema:
                type: object
                properties:
                  url:
                    type: string
                  link:
                    $ref: '#/components/schemas/ShareLink'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Invalid'
  /documents/{document}/links/{link}:
    delete:
      x-scope: write
      tags:
        - Documents
      summary: Take a link back
      description: End a share link. Whoever holds the address sees nothing from then on; the audit row says who ended it.
      operationId: revokeShareLink
      parameters:
        - $ref: '#/components/parameters/document'
        - name: link
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: The link, ended.
          content:
            application/json:
              schema:
                type: object
                properties:
                  link:
                    $ref: '#/components/schemas/ShareLink'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
  /checkup/obligations/{obligation}:
    post:
      x-scope: write
      tags:
        - Checkup
      summary: Act on a dated item
      description: >-
        `handled` closes it; `snooze` puts it off `until` a day; `dismiss` closes it as not needed; `reopen` brings it
        back; `manual` says a bill Thaw took for automatic is paid by hand, and the payee remembers.
      operationId: actOnObligation
      parameters:
        - name: obligation
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - action
              properties:
                action:
                  type: string
                  enum:
                    - handled
                    - snooze
                    - dismiss
                    - reopen
                    - manual
                until:
                  type: string
                  format: date
                  description: With `snooze`; after today.
      responses:
        '200':
          description: The item as it stands.
          content:
            application/json:
              schema:
                type: object
                properties:
                  item:
                    $ref: '#/components/schemas/CheckupItem'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Invalid'
  /checkup/obligations/{obligation}/lead-days:
    patch:
      x-scope: write
      tags:
        - Checkup
      summary: How far ahead to raise it
      description: The days before its date that this item shows as due, from 1 to 365.
      operationId: setLeadDays
      parameters:
        - name: obligation
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - days
              properties:
                days:
                  type: integer
                  minimum: 1
                  maximum: 365
      responses:
        '200':
          description: The item.
          content:
            application/json:
              schema:
                type: object
                properties:
                  item:
                    $ref: '#/components/schemas/CheckupItem'
                  lead_days:
                    type: integer
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Invalid'
  /checkup/{gap}/dismiss:
    post:
      x-scope: write
      tags:
        - Checkup
      summary: It's fine
      description: >-
        Something Thaw thought was missing is not needed, with a reason in the person's words. `disposed` also says the
        thing it is about is gone (sold, ended), which closes everything else about it.
      operationId: dismissGap
      parameters:
        - name: gap
          in: path
          required: true
          schema:
            type: string
            format: uuid
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - reason
              properties:
                reason:
                  type: string
                  maxLength: 300
                disposed:
                  type: boolean
      responses:
        '200':
          description: The item.
          content:
            application/json:
              schema:
                type: object
                properties:
                  item:
                    $ref: '#/components/schemas/CheckupItem'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/Invalid'
  /checkup/{gap}/reopen:
    post:
      x-scope: write
      tags:
        - Checkup
      summary: Ask again
      description: Something dismissed is back on the Checkup.
      operationId: reopenGap
      parameters:
        - name: gap
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: The item.
          content:
            application/json:
              schema:
                type: object
                properties:
                  item:
                    $ref: '#/components/schemas/CheckupItem'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '404':
          $ref: '#/components/responses/NotFound'
  /reminders:
    post:
      x-scope: write
      tags:
        - Checkup
      summary: Remind me
      description: A dated item of the person's own, on a day ahead, about a document or a person or thing if named.
      operationId: makeReminder
      parameters:
        - $ref: '#/components/parameters/idempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - title
                - 'on'
              properties:
                title:
                  type: string
                  minLength: 3
                  maxLength: 120
                'on':
                  type: string
                  format: date
                  description: After today.
                document_id:
                  type: string
                  format: uuid
                person_or_thing:
                  type: string
                  maxLength: 120
                  description: An id or a name.
                note:
                  type: string
                  maxLength: 600
      responses:
        '201':
          description: The item.
          content:
            application/json:
              schema:
                type: object
                properties:
                  item:
                    $ref: '#/components/schemas/CheckupItem'
        '401':
          $ref: '#/components/responses/NotSignedIn'
        '403':
          $ref: '#/components/responses/NotAllowed'
        '422':
          $ref: '#/components/responses/Invalid'
components:
  securitySchemes:
    bearer:
      type: http
      scheme: bearer
      description: >-
        A personal token (`thaw_pat_…`, from Settings > Developers) or an assistant connection's access token. With a
        personal token, `Thaw-Account` names the family or business.
  parameters:
    document:
      name: document
      in: path
      required: true
      schema:
        type: string
        format: uuid
    entity:
      name: entity
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: A person or thing's id.
    bundle:
      name: bundle
      in: path
      required: true
      schema:
        type: string
        format: uuid
    import:
      name: import
      in: path
      required: true
      schema:
        type: string
        format: uuid
    file:
      name: file
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: One of the document's files, from `files` on the document.
    category:
      name: category
      in: query
      schema:
        type: string
        maxLength: 80
      description: A category's slug or name.
    type:
      name: type
      in: query
      schema:
        type: string
        maxLength: 120
      description: A type's slug or name.
    personOrThing:
      name: person_or_thing
      in: query
      schema:
        type: string
        maxLength: 120
      description: A person or thing's id or name.
    year:
      name: year
      in: query
      schema:
        type: integer
        minimum: 1900
        maximum: 2100
      description: The year on the document.
    includeSuperseded:
      name: include_superseded
      in: query
      schema:
        type: boolean
        default: false
      description: Include documents replaced by a newer version.
    idempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        maxLength: 200
      description: >-
        Any string of the caller's choosing. The same key with the same body within a day replays the first answer,
        marked `Idempotent-Replayed: true`; with a different body it is 422; while the first call is still running it is
        409.
  responses:
    Draft:
      description: The draft.
      content:
        application/json:
          schema:
            type: object
            properties:
              to:
                type:
                  - string
                  - 'null'
              subject:
                type: string
              body:
                type: string
              needs:
                type: array
                items:
                  type: string
                description: Placeholders left to fill by hand.
    Problem:
      description: A refusal, as a problem.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    BadRequest:
      description: The request could not be read (a cursor that is not one, for instance).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    NotSignedIn:
      description: >-
        No token, a token that has ended, or one issued for the other door. `WWW-Authenticate` names the resource
        metadata document.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    NotAllowed:
      description: >-
        The token, the role or the plan does not allow it. A missing scope is named in `scope` and in the
        `WWW-Authenticate` header as `insufficient_scope`; a plan that does not come with it says which one does.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    NotFound:
      description: Nothing of that id in this family or business.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/Problem'
    Invalid:
      description: A field was wrong. `errors` names each one.
      content:
        application/problem+json:
          schema:
            allOf:
              - $ref: '#/components/schemas/Problem'
              - type: object
                properties:
                  errors:
                    type: object
                    additionalProperties:
                      type: array
                      items:
                        type: string
  schemas:
    Problem:
      type: object
      required:
        - type
        - title
        - status
      properties:
        type:
          type: string
          description: '`about:blank`, or a URL under the API host naming the kind of problem.'
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
          description: One sentence a person could read out.
        scope:
          $ref: '#/components/schemas/Scope'
          description: The scope the call needed, on a 403 for want of one.
    Scope:
      type: string
      enum:
        - read
        - write
        - files
        - identifiers
        - ask
        - manage
      description: >-
        `read` (search, documents, facts, people & things, the Checkup, bundles, imports; every token has it), `write`
        (change things), `files` (the original file and the pages), `identifiers` (account, policy and license numbers),
        `ask` (Thaw's answer and the drafts). `manage` is on Thaw's own app's token alone and is never offered on a
        personal one.
    Named:
      type: object
      properties:
        id:
          type: string
        title:
          type: string
        name:
          type: string
        kind:
          type: string
        url:
          type: string
          description: The page in Thaw.
    Status:
      type: object
      description: The one line Home shows for a person or thing.
      properties:
        text:
          type: string
        when:
          type:
            - string
            - 'null'
          format: date
        more:
          type: integer
          description: How many other open items there are about it.
    DocumentRow:
      type: object
      description: One document as a list shows it.
      properties:
        id:
          type: string
        title:
          type: string
        type:
          type:
            - string
            - 'null'
        category:
          type:
            - string
            - 'null'
        date:
          type:
            - string
            - 'null'
          format: date
          description: The date printed on it.
        period:
          type:
            - object
            - 'null'
          properties:
            from:
              type:
                - string
                - 'null'
            to:
              type:
                - string
                - 'null'
        status:
          type: string
          description: In words - Filed, Reading, Needs a look and so on.
        needs_look:
          type: boolean
        read:
          type: string
          enum:
            - full
            - quick
            - not yet
        superseded:
          type: boolean
          description: Replaced by a newer version.
        duplicate:
          type: boolean
          description: A copy of one already on file.
        people_and_things:
          type: array
          items:
            allOf:
              - $ref: '#/components/schemas/Named'
              - type: object
                properties:
                  role:
                    type:
                      - string
                      - 'null'
        arrived:
          type:
            - string
            - 'null'
          format: date
        url:
          type: string
    Document:
      type: object
      description: >-
        The whole document, as `GET /documents/{id}` returns it - the text at the top for a reader, everything else
        under `metadata`.
      properties:
        id:
          type: string
        title:
          type: string
        text:
          type:
            - string
            - 'null'
          description: The window of words, page headings inline; null unless `text=1` was asked for.
        url:
          type: string
        metadata:
          allOf:
            - $ref: '#/components/schemas/DocumentRow'
            - type: object
              properties:
                summary:
                  type:
                    - string
                    - 'null'
                confidence:
                  type:
                    - integer
                    - 'null'
                page_count:
                  type:
                    - integer
                    - 'null'
                read_by_ocr_pages:
                  type: integer
                retention_until:
                  type:
                    - string
                    - 'null'
                  format: date
                corrected_by_hand:
                  type: array
                  items:
                    type: string
                  description: The fields a person set, which no read touches again.
                arrived_by:
                  type:
                    - string
                    - 'null'
                  description: The channel it came in on.
                facts:
                  type: array
                  items:
                    $ref: '#/components/schemas/Fact'
                identifiers_shown:
                  type: boolean
                up_next:
                  type: array
                  items:
                    $ref: '#/components/schemas/UpNext'
                replaces:
                  type: array
                  items:
                    $ref: '#/components/schemas/Named'
                replaced_by:
                  oneOf:
                    - $ref: '#/components/schemas/Named'
                    - type: 'null'
                duplicate_of:
                  oneOf:
                    - $ref: '#/components/schemas/Named'
                    - type: 'null'
                links_out:
                  type: array
                  items:
                    type: object
                  description: The document's share links, without their tokens.
                text_window:
                  type:
                    - object
                    - 'null'
                  properties:
                    from_page:
                      type:
                        - integer
                        - 'null'
                    to_page:
                      type:
                        - integer
                        - 'null'
                    total_pages:
                      type: integer
                    more:
                      type: boolean
                    next_page:
                      type:
                        - integer
                        - 'null'
    Fact:
      type: object
      properties:
        id:
          type: string
        key:
          type: string
        label:
          type:
            - string
            - 'null'
        role:
          type: string
          enum:
            - money
            - date
            - party
            - text
            - identifier
        value:
          type:
            - string
            - 'null'
        number:
          type:
            - number
            - 'null'
        date:
          type:
            - string
            - 'null'
          format: date
        currency:
          type:
            - string
            - 'null'
        page:
          type:
            - integer
            - 'null'
        corrected_by_hand:
          type: boolean
    UpNext:
      type: object
      description: A dated item - a payment, a renewal, an expiry, a filing.
      properties:
        id:
          type: string
        kind:
          type: string
        kind_label:
          type: string
        title:
          type: string
        due:
          type: string
          format: date
        status:
          type: string
        snoozed_until:
          type:
            - string
            - 'null'
          format: date
        amount:
          type:
            - number
            - 'null'
        currency:
          type:
            - string
            - 'null'
        if_missed:
          type:
            - string
            - 'null'
        what_to_do:
          type:
            - object
            - 'null'
          properties:
            summary:
              type:
                - string
                - 'null'
            steps:
              type: array
              items:
                type: string
            phone:
              type:
                - string
                - 'null'
            links:
              type: array
              items:
                type: string
            prepared:
              type:
                - string
                - 'null'
        document:
          oneOf:
            - $ref: '#/components/schemas/Named'
            - type: 'null'
        person_or_thing:
          oneOf:
            - $ref: '#/components/schemas/Named'
            - type: 'null'
        url:
          type: string
    CheckupItem:
      type: object
      description: One line of the Checkup - a dated item, something missing, or a document waiting on a look.
      properties:
        id:
          type: string
        source:
          type: string
          enum:
            - obligation
            - gap
            - document
        kind:
          type: string
        kind_label:
          type: string
        title:
          type: string
        detail:
          type:
            - string
            - 'null'
        when:
          type:
            - string
            - 'null'
          format: date
        overdue:
          type: boolean
        group:
          type:
            - string
            - 'null'
        severity:
          type:
            - string
            - 'null'
        status:
          type: string
        snoozed_until:
          type:
            - string
            - 'null'
        dismissed_reason:
          type:
            - string
            - 'null'
        amount:
          type:
            - number
            - 'null'
        currency:
          type:
            - string
            - 'null'
        what_to_do:
          type:
            - object
            - string
            - 'null'
        person_or_thing:
          oneOf:
            - $ref: '#/components/schemas/Named'
            - type: 'null'
        document:
          oneOf:
            - $ref: '#/components/schemas/Named'
            - type: 'null'
        url:
          type: string
    PersonOrThing:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        kind:
          type: string
          enum:
            - person
            - pet
            - organization
            - property
            - vehicle
            - financial_account
            - policy
            - asset
        kind_label:
          type: string
        subtitle:
          type:
            - string
            - 'null'
        also_known_as:
          type: array
          items:
            type: string
        details:
          type: object
          description: What is known about it; the numbers someone could use are left out without the `identifiers` scope.
        gone:
          type:
            - string
            - 'null'
          format: date
          description: When it was sold, ended or said to be not yours.
        url:
          type: string
    ShareLink:
      type: object
      description: A link out, without its token.
      properties:
        id:
          type: string
        expires:
          type: string
          format: date
        views:
          type: integer
        max_views:
          type:
            - integer
            - 'null'
        passcode:
          type: boolean
        live:
          type: boolean
        revoked:
          type: boolean
