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.
  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.
                  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.
        "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.
      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.
      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.

  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`.
      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 #!high due:tomorrow
        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
