{
  "openapi": "3.1.0",
  "info": {
    "title": "tobis.vision — Tobi Moccagatta content API",
    "version": "1.0.0",
    "summary": "Read-only access to the content behind tobis.vision, plus the site's machine-readable discovery files.",
    "description": "The portfolio of Tobi Moccagatta, Creative Developer at basement.studio.\n\nEvery operation is a GET, every response is public, and no credentials exist to send. Writes go through the CMS admin and are not part of this description.\n\n## Versioning\n\n- Versions live in the URL path. `/api/v1/…` is the current major and the one to integrate against.\n- `/api/…` is an unversioned alias of whatever major is current — convenient for a one-off read, wrong for anything that has to keep working.\n- A breaking change ships as a new major at a new path. The old path keeps working through its deprecation window.\n- Adding a field, an endpoint or an optional parameter is not breaking, and happens without a version bump.\n\n## Deprecation\n\n- A retiring version answers with `Deprecation` (RFC 9745) carrying the date the deprecation took effect, and `Sunset` (RFC 8594) carrying the date it stops answering.\n- Both come with `Link: <…>; rel=\"deprecation\"` pointing at the notice.\n- The window between the two is at least six months.\n- Nothing is deprecated today, so neither header is being sent.\n\n## Rate limits\n\n- 120 requests per 60 seconds per client, on GET and HEAD.\n- Every response carries `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`, and the structured-field `RateLimit` and `RateLimit-Policy` the current draft defines. Read either.\n- Over the limit is `429` with `Retry-After` in seconds.\n- Counters are held per server instance, so the effective limit across a fleet is higher than the number quoted. Pace against the headers, not the number.\n\nHuman-readable index of the same surface: https://tobis.vision/developers\nAgent instructions: https://tobis.vision/agents.md",
    "x-api-major": 1,
    "x-unversioned-alias": "/api",
    "contact": {
      "name": "Tobi Moccagatta",
      "email": "contact@tobis.vision",
      "url": "https://tobis.vision"
    },
    "license": {
      "name": "All rights reserved",
      "identifier": "LicenseRef-All-Rights-Reserved"
    }
  },
  "servers": [
    {
      "url": "https://tobis.vision",
      "description": "Production. Paths below are the versioned ones; `/api/…` answers identically today and is not pinned."
    }
  ],
  "tags": [
    {
      "name": "Content",
      "description": "The documents the pages are built from."
    },
    {
      "name": "Discovery",
      "description": "Files whose whole purpose is to be found and read by a machine."
    },
    {
      "name": "Pages",
      "description": "The site's own pages, as markdown."
    }
  ],
  "paths": {
    "/api/v1/works": {
      "get": {
        "operationId": "listWorks",
        "summary": "List published work",
        "description": "Filtering uses Payload's query syntax — `where[year][greater_than]=2023`, `where[kind][equals]=client`.",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Documents per page. `0` returns every document.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 10
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-indexed page number.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Field to sort by. Prefix with `-` to reverse, comma-separate for tie-breaks — e.g. `order,-year`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "depth",
            "in": "query",
            "required": false,
            "description": "How many levels of related documents to resolve. `0` leaves relationships as ids; `2` inlines the media a work points at.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 10,
              "default": 2
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of works.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkList"
                }
              }
            }
          },
          "429": {
            "description": "Over the limit. Retry after the number of seconds in `Retry-After`. Counters are per server instance, so this can arrive earlier or later than 120 requests would suggest.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/works/{id}": {
      "get": {
        "operationId": "getWork",
        "summary": "Fetch one work by id",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Numeric document id, as returned by `/api/works`.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "depth",
            "in": "query",
            "required": false,
            "description": "How many levels of related documents to resolve. `0` leaves relationships as ids; `2` inlines the media a work points at.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 10,
              "default": 2
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The work.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Work"
                }
              }
            }
          },
          "404": {
            "description": "No such document. The body is a Payload error envelope, not the site's HTML 404.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Over the limit. Retry after the number of seconds in `Retry-After`. Counters are per server instance, so this can arrive earlier or later than 120 requests would suggest.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/media": {
      "get": {
        "operationId": "listMedia",
        "summary": "List images and video",
        "description": "Every asset carries `alt`, and images carry `width`/`height`. Videos are stored as separate mp4/webm/poster documents referenced by a work's media blocks.",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Documents per page. `0` returns every document.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 10
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-indexed page number.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "description": "Field to sort by. Prefix with `-` to reverse, comma-separate for tie-breaks — e.g. `order,-year`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "depth",
            "in": "query",
            "required": false,
            "description": "How many levels of related documents to resolve. `0` leaves relationships as ids; `2` inlines the media a work points at.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 10,
              "default": 2
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of media documents.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MediaList"
                }
              }
            }
          },
          "429": {
            "description": "Over the limit. Retry after the number of seconds in `Retry-After`. Counters are per server instance, so this can arrive earlier or later than 120 requests would suggest.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/media/{id}": {
      "get": {
        "operationId": "getMedia",
        "summary": "Fetch one media document by id",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The media document.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Media"
                }
              }
            }
          },
          "404": {
            "description": "No such document. The body is a Payload error envelope, not the site's HTML 404.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Over the limit. Retry after the number of seconds in `Retry-After`. Counters are per server instance, so this can arrive earlier or later than 120 requests would suggest.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/globals/home": {
      "get": {
        "operationId": "getHomeGlobal",
        "summary": "Home page copy",
        "tags": [
          "Content"
        ],
        "parameters": [
          {
            "name": "depth",
            "in": "query",
            "required": false,
            "description": "How many levels of related documents to resolve. `0` leaves relationships as ids; `2` inlines the media a work points at.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 10,
              "default": 2
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The home global.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Home"
                }
              }
            }
          },
          "429": {
            "description": "Over the limit. Retry after the number of seconds in `Retry-After`. Counters are per server instance, so this can arrive earlier or later than 120 requests would suggest.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/globals/about": {
      "get": {
        "operationId": "getAboutGlobal",
        "summary": "The bio, as Lexical rich text",
        "description": "For the same text already rendered, read https://tobis.vision/about.md.",
        "tags": [
          "Content"
        ],
        "responses": {
          "200": {
            "description": "The about global.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/About"
                }
              }
            }
          },
          "429": {
            "description": "Over the limit. Retry after the number of seconds in `Retry-After`. Counters are per server instance, so this can arrive earlier or later than 120 requests would suggest.",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests allowed in the window. Currently 120.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Requests left in the current window.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until the window resets.",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit": {
                "description": "The same figures as a structured-field dictionary — `default;r=119;t=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "RateLimit-Policy": {
                "description": "The policy in force — `default;q=120;w=60`.",
                "schema": {
                  "type": "string"
                }
              },
              "X-API-Version": {
                "description": "Major version that answered.",
                "schema": {
                  "type": "integer"
                }
              },
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "operationId": "getLlmsTxt",
        "summary": "The site in one plain-text file — pages, selected work, experiments, contact.",
        "tags": [
          "Discovery"
        ],
        "responses": {
          "200": {
            "description": "The site in one plain-text file — pages, selected work, experiments, contact.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/sitemap.xml": {
      "get": {
        "operationId": "getSitemapXml",
        "summary": "Every published URL.",
        "tags": [
          "Discovery"
        ],
        "responses": {
          "200": {
            "description": "Every published URL.",
            "content": {
              "application/xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/robots.txt": {
      "get": {
        "operationId": "getRobotsTxt",
        "summary": "Crawler policy. Retrieval and training crawlers are both allowed.",
        "tags": [
          "Discovery"
        ],
        "responses": {
          "200": {
            "description": "Crawler policy. Retrieval and training crawlers are both allowed.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/agents.md": {
      "get": {
        "operationId": "getAgentsMd",
        "summary": "When to use this site, when not to, and how to call it.",
        "tags": [
          "Discovery"
        ],
        "responses": {
          "200": {
            "description": "When to use this site, when not to, and how to call it.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "operationId": "getOpenapiJson",
        "summary": "This document.",
        "tags": [
          "Discovery"
        ],
        "responses": {
          "200": {
            "description": "This document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/{page}.md": {
      "get": {
        "operationId": "getPageMarkdown",
        "summary": "A page as markdown",
        "description": "The same documents are served at each page's own URL when the request carries `Accept: text/markdown`, per acceptmarkdown.com. Those responses set `Vary: Accept, Accept-Encoding`.\n\nExample: `https://tobis.vision/about.md`.",
        "tags": [
          "Pages"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "path",
            "required": true,
            "description": "Page path without its leading slash.",
            "schema": {
              "type": "string",
              "enum": [
                "index",
                "experiments",
                "works",
                "about",
                "developers",
                "experiments/critters",
                "experiments/lpv",
                "experiments/bip",
                "experiments/fluid"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The page, as markdown.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "description": "No such page. The body is a markdown 404 listing every page that does exist.",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Work": {
        "type": "object",
        "description": "One project.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "slug": {
            "type": "string",
            "description": "URL segment. Lowercase, hyphenated."
          },
          "year": {
            "type": "integer"
          },
          "summary": {
            "type": "string",
            "description": "One or two sentences, written to stand alone out of context."
          },
          "body": {
            "anyOf": [
              {
                "type": "object",
                "description": "Lexical rich text, as stored by Payload. `root.children` is an array of block nodes; text lives on `text` nodes underneath. The rendered markdown of the same content is available from the page's markdown representation.",
                "properties": {
                  "root": {
                    "type": "object",
                    "properties": {
                      "type": {
                        "type": "string"
                      },
                      "children": {
                        "type": "array",
                        "items": {
                          "type": "object"
                        }
                      }
                    }
                  }
                },
                "additionalProperties": true
              },
              {
                "type": "null"
              }
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "basement",
              "client",
              "personal"
            ],
            "description": "Who authored it: studio work, freelance for a client, or personal."
          },
          "client": {
            "type": "object",
            "properties": {
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "url": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "stack": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "id": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "name"
              ]
            }
          },
          "role": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "id": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "name"
              ]
            }
          },
          "links": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "label": {
                  "type": "string"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "required": [
                "label",
                "url"
              ]
            }
          },
          "media": {
            "type": "array",
            "description": "Blocks of `blockType: \"image\"` or `\"video\"`. The first is what the card shows. Related media are ids at `depth=0` and inlined documents from `depth=1`.",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "badge": {
            "type": [
              "string",
              "null"
            ]
          },
          "span": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "half",
              "full",
              null
            ]
          },
          "order": {
            "type": [
              "integer",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "title",
          "slug",
          "year",
          "summary",
          "kind"
        ]
      },
      "WorkList": {
        "type": "object",
        "description": "Payload's paginated list envelope.",
        "properties": {
          "docs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Work"
            }
          },
          "totalDocs": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "totalPages": {
            "type": "integer"
          },
          "page": {
            "type": "integer"
          },
          "pagingCounter": {
            "type": "integer"
          },
          "hasPrevPage": {
            "type": "boolean"
          },
          "hasNextPage": {
            "type": "boolean"
          },
          "prevPage": {
            "type": [
              "integer",
              "null"
            ]
          },
          "nextPage": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "docs",
          "totalDocs",
          "limit",
          "page",
          "totalPages"
        ]
      },
      "Media": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "alt": {
            "type": "string",
            "description": "What it shows, in a sentence."
          },
          "url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "filename": {
            "type": [
              "string",
              "null"
            ]
          },
          "mimeType": {
            "type": [
              "string",
              "null"
            ]
          },
          "filesize": {
            "type": [
              "integer",
              "null"
            ]
          },
          "width": {
            "type": [
              "integer",
              "null"
            ]
          },
          "height": {
            "type": [
              "integer",
              "null"
            ]
          },
          "thumbnailURL": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "alt"
        ]
      },
      "MediaList": {
        "type": "object",
        "description": "Payload's paginated list envelope.",
        "properties": {
          "docs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Media"
            }
          },
          "totalDocs": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "totalPages": {
            "type": "integer"
          },
          "page": {
            "type": "integer"
          },
          "pagingCounter": {
            "type": "integer"
          },
          "hasPrevPage": {
            "type": "boolean"
          },
          "hasNextPage": {
            "type": "boolean"
          },
          "prevPage": {
            "type": [
              "integer",
              "null"
            ]
          },
          "nextPage": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "docs",
          "totalDocs",
          "limit",
          "page",
          "totalPages"
        ]
      },
      "Home": {
        "type": "object",
        "properties": {
          "greeting": {
            "type": "string",
            "description": "The page's h1."
          },
          "intro": {
            "type": "string"
          },
          "availability": {
            "type": "string"
          },
          "availableLead": {
            "type": "string"
          },
          "services": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "id": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "name"
              ]
            }
          },
          "featured": {
            "type": "array",
            "description": "Works shown in the home carousel, in order. Ids at `depth=0`, documents above it.",
            "items": {
              "anyOf": [
                {
                  "type": "integer"
                },
                {
                  "$ref": "#/components/schemas/Work"
                }
              ]
            }
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "greeting",
          "intro",
          "availability"
        ]
      },
      "About": {
        "type": "object",
        "properties": {
          "bio": {
            "type": "object",
            "description": "Lexical rich text, as stored by Payload. `root.children` is an array of block nodes; text lives on `text` nodes underneath. The rendered markdown of the same content is available from the page's markdown representation.",
            "properties": {
              "root": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string"
                  },
                  "children": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  }
                }
              }
            },
            "additionalProperties": true
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "bio"
        ]
      },
      "Error": {
        "type": "object",
        "properties": {
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "message": {
                  "type": "string"
                }
              },
              "required": [
                "message"
              ]
            }
          }
        },
        "required": [
          "errors"
        ]
      }
    }
  },
  "externalDocs": {
    "description": "Developer resources for tobis.vision",
    "url": "https://tobis.vision/developers"
  }
}
