{
  "openapi": "3.1.0",
  "info": {
    "title": "Yapture API",
    "version": "1.0",
    "description": "Real-time collaborative list platform. Every list is a CRDT document synced over WebSocket. Create lists, manage yaps (items), control permissions, and sync state — all without a user account.\n",
    "contact": {
      "name": "Yapture",
      "url": "https://yapture.com"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.yapture.com/v1",
      "description": "Production"
    },
    {
      "url": "https://dev.app.yapture.com/api/v1",
      "description": "Development"
    },
    {
      "url": "http://localhost:4736/api/v1",
      "description": "Localhost"
    }
  ],
  "paths": {
    "/lists": {
      "post": {
        "operationId": "createList",
        "summary": "Create a list",
        "description": "Create a new list. No authentication required.",
        "tags": [
          "Lists"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "description": "Display title for the list",
                    "example": "grocery run"
                  },
                  "kind": {
                    "$ref": "#/components/schemas/ListKind"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "List created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ref": {
                      "type": "string",
                      "description": "Opaque list reference",
                      "example": "groc-7k2x"
                    },
                    "ownerCode": {
                      "type": "string",
                      "description": "Bearer token granting owner role. Save this — it cannot be retrieved again.\n"
                    },
                    "list": {
                      "$ref": "#/components/schemas/List"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/lists/{ref}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Ref"
        }
      ],
      "get": {
        "operationId": "getList",
        "summary": "Resolve list + caller role",
        "description": "Retrieve list metadata and the caller's role.",
        "tags": [
          "Lists"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "responses": {
          "200": {
            "description": "List resolved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "list": {
                      "$ref": "#/components/schemas/List"
                    },
                    "role": {
                      "$ref": "#/components/schemas/ListRole"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "updateList",
        "summary": "Update list metadata",
        "tags": [
          "Lists"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string"
                  },
                  "kind": {
                    "$ref": "#/components/schemas/ListKind"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "List updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "archiveList",
        "summary": "Archive list",
        "tags": [
          "Lists"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "responses": {
          "204": {
            "description": "List archived"
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/lists/{ref}/yaps": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Ref"
        }
      ],
      "get": {
        "operationId": "listYaps",
        "summary": "List yaps",
        "tags": [
          "Yaps"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "responses": {
          "200": {
            "description": "Yaps retrieved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "yaps": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Yap"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createYap",
        "summary": "Create a yap",
        "tags": [
          "Yaps"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Yap"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Yap created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Yap"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/lists/{ref}/yaps/{yapId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Ref"
        },
        {
          "name": "yapId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Yap identifier"
        }
      ],
      "patch": {
        "operationId": "updateYap",
        "summary": "Update yap",
        "tags": [
          "Yaps"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Yap"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Yap updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Yap"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Yap or list not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteYap",
        "summary": "Delete yap",
        "tags": [
          "Yaps"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "responses": {
          "204": {
            "description": "Yap deleted"
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Yap or list not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/lists/{ref}/permissions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Ref"
        }
      ],
      "get": {
        "operationId": "listPermissions",
        "summary": "List permissions",
        "tags": [
          "Permissions"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "responses": {
          "200": {
            "description": "Permissions retrieved",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "permissions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Permission"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "issuePermission",
        "summary": "Issue permission",
        "tags": [
          "Permissions"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "role"
                ],
                "properties": {
                  "role": {
                    "$ref": "#/components/schemas/ListRole"
                  },
                  "label": {
                    "type": "string",
                    "description": "Human-readable label for this permission",
                    "example": "kitchen-agent"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Permission issued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "permission": {
                      "$ref": "#/components/schemas/Permission"
                    },
                    "code": {
                      "type": "string",
                      "description": "Bearer token for this permission. Save this — it cannot be retrieved again.\n"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/lists/{ref}/permissions/{permissionId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Ref"
        },
        {
          "name": "permissionId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Permission identifier"
        }
      ],
      "delete": {
        "operationId": "revokePermission",
        "summary": "Revoke permission",
        "tags": [
          "Permissions"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "responses": {
          "204": {
            "description": "Permission revoked"
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Permission or list not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/lists/{ref}/permissions/rotate-owner": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Ref"
        }
      ],
      "post": {
        "operationId": "rotateOwnerCode",
        "summary": "Rotate owner code",
        "description": "Invalidates the current owner code and returns a new one.\n",
        "tags": [
          "Permissions"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "responses": {
          "200": {
            "description": "Owner code rotated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ownerCode": {
                      "type": "string",
                      "description": "New bearer token for the owner role"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/lists/{ref}/rollup": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Ref"
        }
      ],
      "post": {
        "operationId": "rollupList",
        "summary": "Merge into user account",
        "description": "Merge an anonymous list into an authenticated user account.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "responses": {
          "200": {
            "description": "List merged into account",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "merged": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/lists/{ref}/snapshot": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Ref"
        }
      ],
      "get": {
        "operationId": "getSnapshot",
        "summary": "Download Yjs state",
        "description": "Download the current Yjs CRDT state as a binary snapshot.",
        "tags": [
          "Sync"
        ],
        "security": [
          {
            "permCode": []
          }
        ],
        "responses": {
          "200": {
            "description": "Yjs state snapshot",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/lists/{ref}/sync": {
      "parameters": [
        {
          "$ref": "#/components/parameters/Ref"
        }
      ],
      "get": {
        "operationId": "syncWebSocket",
        "summary": "WebSocket sync",
        "description": "y-websocket sync endpoint. Upgrade to WebSocket. Authenticate via the `k` query parameter.\n",
        "tags": [
          "Sync"
        ],
        "parameters": [
          {
            "name": "k",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Permission code for WebSocket authentication"
          }
        ],
        "responses": {
          "101": {
            "description": "Switching Protocols — WebSocket upgrade"
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "List not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "Ref": {
        "name": "ref",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Opaque list reference (e.g. `groc-7k2x`)",
        "example": "groc-7k2x"
      }
    },
    "securitySchemes": {
      "permCode": {
        "type": "http",
        "scheme": "bearer",
        "description": "Permission code issued at list creation (ownerCode) or via the POST /lists/{ref}/permissions endpoint.\n"
      }
    },
    "schemas": {
      "ListKind": {
        "type": "string",
        "enum": [
          "task",
          "note",
          "event"
        ],
        "description": "The kind of list"
      },
      "ListRole": {
        "type": "string",
        "enum": [
          "owner",
          "editor",
          "viewer",
          "agent"
        ],
        "description": "Role a caller holds on a list"
      },
      "List": {
        "type": "object",
        "properties": {
          "ref": {
            "type": "string",
            "description": "Opaque list reference",
            "example": "groc-7k2x"
          },
          "name": {
            "type": "string",
            "example": "grocery run"
          },
          "kind": {
            "$ref": "#/components/schemas/ListKind"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Yap": {
        "description": "A yap is an item in a list. Discriminated by `kind`.\n",
        "oneOf": [
          {
            "$ref": "#/components/schemas/TaskYap"
          },
          {
            "$ref": "#/components/schemas/NoteYap"
          },
          {
            "$ref": "#/components/schemas/EventYap"
          }
        ],
        "discriminator": {
          "propertyName": "kind",
          "mapping": {
            "task": "#/components/schemas/TaskYap",
            "note": "#/components/schemas/NoteYap",
            "event": "#/components/schemas/EventYap"
          }
        }
      },
      "TaskYap": {
        "type": "object",
        "required": [
          "id",
          "listId",
          "kind",
          "content",
          "status",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "listId": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "const": "task"
          },
          "content": {
            "type": "string",
            "example": "Buy milk"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "in_progress",
              "done",
              "cancelled"
            ]
          },
          "priority": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high",
              "urgent"
            ],
            "nullable": true
          },
          "dueAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "NoteYap": {
        "type": "object",
        "required": [
          "id",
          "listId",
          "kind",
          "body",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "listId": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "const": "note"
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "body": {
            "type": "string",
            "example": "Remember to check expiry dates"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EventYap": {
        "type": "object",
        "required": [
          "id",
          "listId",
          "kind",
          "title",
          "startsAt",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "listId": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "const": "event"
          },
          "title": {
            "type": "string",
            "example": "Farmers market Saturday"
          },
          "startsAt": {
            "type": "string",
            "format": "date-time"
          },
          "endsAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "location": {
            "type": "string",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Permission": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "role": {
            "$ref": "#/components/schemas/ListRole"
          },
          "label": {
            "type": "string",
            "example": "kitchen-agent"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "RFC 7807 Problem Details",
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "example": "https://yapture.com/problems/not-found"
          },
          "title": {
            "type": "string",
            "example": "Not Found"
          },
          "status": {
            "type": "integer",
            "example": 404
          },
          "detail": {
            "type": "string",
            "example": "List groc-7k2x not found"
          },
          "instance": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    }
  }
}
