{
  "openapi": "3.1.0",
  "info": {
    "title": "Scorekeeper Agent API",
    "version": "1.0.0",
    "description": "Generic two-team scoreboards. Boards are public through spectator links. Keep bearer and handoff tokens private. Free boards include branding and 50 total spectator views. Persist requestId for identical retries; use current expectedRevision for writes. See /agents-guide.md for recovery."
  },
  "servers": [
    {
      "url": "https://www.scorekeeper.co"
    }
  ],
  "paths": {
    "/api/v1/agent-sessions": {
      "post": {
        "operationId": "createGuestSession",
        "summary": "Create a seven-day guest session for one board",
        "security": [],
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "access_token": {
                      "type": "string"
                    },
                    "token_type": {
                      "type": "string"
                    },
                    "expires_in": {
                      "type": "integer"
                    },
                    "scope": {
                      "type": "string"
                    },
                    "limits": {
                      "type": "object",
                      "properties": {
                        "boards": {
                          "type": "integer"
                        },
                        "session_days": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "boards",
                        "session_days"
                      ]
                    }
                  },
                  "required": [
                    "access_token",
                    "token_type",
                    "expires_in",
                    "scope",
                    "limits"
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; honor Retry-After"
          }
        }
      }
    },
    "/api/v1/scoreboards": {
      "get": {
        "operationId": "listScoreboards",
        "summary": "List owned API-created generic scoreboards",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "scoreboards:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "scoreboards": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Scoreboard"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "scoreboards",
                    "nextCursor"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Reconnect or refresh authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope or board quota",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Board not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision or idempotency conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,128}$"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ]
      },
      "post": {
        "operationId": "createScoreboard",
        "summary": "Create a public two-team scoreboard at zero",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "scoreboards:write"
            ]
          }
        ],
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Scoreboard"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Reconnect or refresh authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope or board quota",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Board not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision or idempotency conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateScoreboard"
              }
            }
          }
        }
      }
    },
    "/api/v1/scoreboards/{id}": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,128}$"
          }
        }
      ],
      "get": {
        "operationId": "getScoreboard",
        "summary": "Read current scores and revision",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "scoreboards:read"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Scoreboard"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Reconnect or refresh authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope or board quota",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Board not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision or idempotency conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "updateScoreboard",
        "summary": "Atomically update scores, title or names",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "scoreboards:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Scoreboard"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Reconnect or refresh authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope or board quota",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Board not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision or idempotency conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateScoreboard"
              }
            }
          }
        }
      }
    },
    "/api/v1/scoreboards/{id}/handoff": {
      "parameters": [
        {
          "in": "path",
          "name": "id",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{1,128}$"
          }
        }
      ],
      "post": {
        "operationId": "handoffScoreboard",
        "summary": "Get a private human handoff; guest claim transfers ownership",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "oauth2": [
              "scoreboards:write"
            ]
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Handoff"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Reconnect or refresh authentication",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope or board quota",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Board not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Revision or idempotency conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited; honor Retry-After",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "requestId": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9_-]{8,128}$"
                  }
                },
                "required": [
                  "requestId"
                ]
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      },
      "oauth2": {
        "type": "oauth2",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://www.scorekeeper.co/agents/connect",
            "tokenUrl": "https://www.scorekeeper.co/api/agents/oauth/token",
            "scopes": {
              "scoreboards:read": "Read owned agent scoreboards",
              "scoreboards:write": "Create and change owned agent scoreboards"
            }
          }
        }
      }
    },
    "schemas": {
      "CreateScoreboard": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "requestId": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{8,128}$"
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "teams": {
            "type": "array",
            "minItems": 2,
            "maxItems": 2,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100
                }
              },
              "required": [
                "name"
              ]
            }
          }
        },
        "required": [
          "requestId",
          "title",
          "teams"
        ]
      },
      "UpdateScoreboard": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "requestId": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{8,128}$"
          },
          "expectedRevision": {
            "type": "string",
            "pattern": "^\\d+:\\d+$"
          },
          "scores": {
            "type": "array",
            "minItems": 1,
            "maxItems": 2,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "teamId": {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9_-]{1,128}$"
                },
                "score": {
                  "type": "integer",
                  "minimum": -999999,
                  "maximum": 999999
                }
              },
              "required": [
                "teamId",
                "score"
              ]
            }
          },
          "increments": {
            "type": "array",
            "minItems": 1,
            "maxItems": 2,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "teamId": {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9_-]{1,128}$"
                },
                "delta": {
                  "type": "integer",
                  "minimum": -999999,
                  "maximum": 999999
                }
              },
              "required": [
                "teamId",
                "delta"
              ]
            }
          },
          "names": {
            "type": "array",
            "minItems": 1,
            "maxItems": 2,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "teamId": {
                  "type": "string",
                  "pattern": "^[A-Za-z0-9_-]{1,128}$"
                },
                "name": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 100
                }
              },
              "required": [
                "teamId",
                "name"
              ]
            }
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          }
        },
        "required": [
          "requestId",
          "expectedRevision"
        ],
        "allOf": [
          {
            "not": {
              "required": [
                "scores",
                "increments"
              ]
            }
          },
          {
            "anyOf": [
              {
                "required": [
                  "scores"
                ]
              },
              {
                "required": [
                  "increments"
                ]
              },
              {
                "required": [
                  "names"
                ]
              },
              {
                "required": [
                  "title"
                ]
              }
            ]
          }
        ]
      },
      "Scoreboard": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "teams": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "score": {
                  "type": "integer",
                  "minimum": -999999,
                  "maximum": 999999
                }
              },
              "required": [
                "id",
                "name",
                "score"
              ]
            }
          },
          "revision": {
            "type": "string"
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "spectator": {
                "type": "string",
                "format": "uri"
              },
              "manage": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "overlay": {
                "type": "string",
                "format": "uri"
              }
            },
            "required": [
              "spectator",
              "manage",
              "overlay"
            ]
          },
          "limits": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "spectatorViews": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "watermark": {
                "type": "boolean"
              }
            },
            "required": [
              "spectatorViews",
              "watermark"
            ]
          },
          "capabilities": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "id",
          "title",
          "teams",
          "revision",
          "links",
          "limits",
          "capabilities"
        ]
      },
      "Handoff": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "url": {
            "type": "string"
          },
          "expiresAt": {
            "type": [
              "integer",
              "null"
            ]
          },
          "transfersOwnership": {
            "type": "boolean"
          }
        },
        "required": [
          "url",
          "expiresAt",
          "transfersOwnership"
        ]
      },
      "Error": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "error": {
            "type": "string"
          },
          "error_description": {
            "type": "string"
          }
        },
        "required": [
          "error",
          "error_description"
        ]
      }
    }
  }
}
