{
  "openapi": "3.1.0",
  "info": {
    "title": "Beancount.io API",
    "description": "Plain-Text Accounting. Powerful, Precise, Auditable. — Plain-text double-entry accounting with git versioning, BQL queries, CSV import, and MCP integration. Use scoped API keys or OAuth 2.1. See /mcp for Model Context Protocol and /graphql for the GraphQL endpoint. Free tier available; sandbox at https://beancount.io/demo.",
    "version": "1.0.0",
    "contact": {
      "name": "Beancount.io",
      "url": "https://beancount.io",
      "email": "hello@beancount.io"
    },
    "license": {
      "name": "MIT",
      "url": "https://opensource.org/license/mit"
    }
  },
  "servers": [
    {
      "url": "https://beancount.io",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    },
    {
      "OAuth2": [
        "read",
        "write"
      ]
    }
  ],
  "tags": [
    {
      "name": "graphql",
      "description": "Beancount.io GraphQL API - blog, docs, and ledger queries"
    },
    {
      "name": "mcp",
      "description": "Model Context Protocol - tool calling for ledgers"
    },
    {
      "name": "ledger",
      "description": "Ledger file and BQL operations"
    }
  ],
  "paths": {
    "/graphql": {
      "post": {
        "operationId": "executeGraphQL",
        "summary": "Execute GraphQL query",
        "description": "Primary API for Beancount.io content and ledger data. Supports 12 operations over 22 types: blogPosts, blogPost, blogTags, researchLogPosts, etc. Requires scoped API key (X-API-Key) or OAuth 2.1 Bearer. Rate limited: 60 req/min; check RateLimit headers and Retry-After on 429. Cost directives documented per operation.",
        "tags": [
          "graphql"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuth2": [
              "read"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "query"
                ],
                "properties": {
                  "query": {
                    "type": "string",
                    "description": "GraphQL query string"
                  },
                  "variables": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Query variables"
                  },
                  "operationName": {
                    "type": "string",
                    "description": "Operation name when query contains multiple operations"
                  }
                }
              },
              "examples": {
                "blogPosts": {
                  "value": {
                    "query": "query { blogPosts(page: 1, pageSize: 5) { posts { slug title } totalCount } }"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "X-API-Key",
            "in": "header",
            "description": "Scoped API key - request only the scopes you need (read, write, admin). Self-serve at /pricing or /contact for sandbox keys.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "GraphQL response",
            "headers": {
              "RateLimit-Limit": {
                "description": "Requests per minute",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Remaining": {
                "description": "Remaining requests in window",
                "schema": {
                  "type": "integer"
                }
              },
              "RateLimit-Reset": {
                "description": "Seconds until window resets",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "object",
                      "nullable": true,
                      "description": "Operation result"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Error"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request - structured JSON error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized - missing or invalid credentials",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited - check Retry-After",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retry",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "graphqlIntrospection",
        "summary": "GraphQL introspection (GET)",
        "description": "Introspection via GET for tooling. Same auth and rate limits as POST. Returns typed schema with 22 types and cost directives.",
        "tags": [
          "graphql"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          },
          {
            "OAuth2": [
              "read"
            ]
          }
        ],
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "GraphQL query string (URL-encoded)"
          }
        ],
        "responses": {
          "200": {
            "description": "Introspection result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api-gateway/mcp": {
      "post": {
        "operationId": "mcpCall",
        "summary": "MCP tool invocation (Streamable HTTP)",
        "description": "Model Context Protocol endpoint implementing Streamable HTTP transport. Tools: runBqlQuery (typed BQL), listLedgerFiles, readLedgerFiles, editLedgerFiles (atomic git commit with dry-run). OAuth 2.1 required with scoped permissions. Returns JSON-RPC 2.0 with structured errors. See https://beancount.io/mcp for setup.",
        "tags": [
          "mcp",
          "ledger"
        ],
        "security": [
          {
            "OAuth2": [
              "read",
              "write"
            ]
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "enum": [
                      "2.0"
                    ]
                  },
                  "id": {
                    "type": "string"
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "tools/list",
                      "tools/call",
                      "initialize",
                      "ping"
                    ]
                  },
                  "params": {
                    "type": "object",
                    "description": "Tool-specific parameters - typed per tool"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "MCP JSON-RPC response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "mcpManifest",
        "summary": "MCP server manifest",
        "description": "Returns MCP server capabilities, tool list, and transport info as JSON. Used by Claude, ChatGPT, and other clients for discovery.",
        "tags": [
          "mcp"
        ],
        "responses": {
          "200": {
            "description": "MCP manifest",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MCPManifest"
                },
                "example": {
                  "name": "beancount",
                  "version": "1.0.0",
                  "transport": "streamable-http",
                  "tools": [
                    {
                      "name": "runBqlQuery",
                      "description": "Run Beancount Query Language query"
                    },
                    {
                      "name": "listLedgerFiles",
                      "description": "List ledger repository files"
                    },
                    {
                      "name": "readLedgerFiles",
                      "description": "Read ledger files"
                    },
                    {
                      "name": "editLedgerFiles",
                      "description": "Atomic git-commit edits with dry-run preview"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/mcp": {
      "get": {
        "operationId": "mcpInfo",
        "summary": "MCP product page and manifest negotiation",
        "description": "Human page at /mcp and machine manifest via Accept: application/json. Returns HTML by default, JSON manifest when Accept includes application/json. Vary: Accept.",
        "tags": [
          "mcp"
        ],
        "parameters": [
          {
            "name": "Accept",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "Use application/json for MCP manifest JSON"
          }
        ],
        "responses": {
          "200": {
            "description": "HTML page or JSON manifest via content negotiation",
            "headers": {
              "Vary": {
                "schema": {
                  "type": "string"
                },
                "description": "Accept"
              }
            },
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MCPManifest"
                }
              }
            }
          }
        }
      }
    },
    "/docs/{slug}": {
      "get": {
        "operationId": "getDoc",
        "summary": "Get documentation page",
        "description": "Docs are available as HTML and via Accept: text/markdown or /docs/{slug}.md suffix. Vary: Accept ensures correct CDN caching.",
        "tags": [
          "ledger"
        ],
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Doc slug, e.g. introduction-to-beancount"
          },
          {
            "name": "Accept",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "text/markdown for markdown variant"
          }
        ],
        "responses": {
          "200": {
            "description": "Doc page",
            "headers": {
              "Vary": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Scoped API key. Roles: read (query content/ledgers), write (ledger edits), admin (full). Request only needed scopes. Self-serve keys via dashboard; free tier and sandbox available at /demo. Rotate via API."
      },
      "OAuth2": {
        "type": "oauth2",
        "description": "OAuth 2.1 with PKCE for MCP and GraphQL. Scopes enforce least privilege.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://beancount.io/oauth/authorize",
            "tokenUrl": "https://beancount.io/oauth/token",
            "scopes": {
              "read": "Read ledgers, queries, and content",
              "write": "Edit ledgers and commit changes",
              "admin": "Full administrative access"
            }
          },
          "clientCredentials": {
            "tokenUrl": "https://beancount.io/oauth/token",
            "scopes": {
              "read": "Read ledgers, queries, and content",
              "write": "Edit ledgers and commit changes"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Machine-readable error code, e.g. BAD_REQUEST, UNAUTHORIZED, RATE_LIMITED"
          },
          "message": {
            "type": "string",
            "description": "Human-readable message"
          },
          "hint": {
            "type": "string",
            "description": "Resolution hint - what to do next"
          },
          "details": {
            "type": "object",
            "additionalProperties": true,
            "description": "Optional structured details"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "$ref": "#/components/schemas/Error"
          }
        }
      },
      "MCPManifest": {
        "type": "object",
        "required": [
          "name",
          "version",
          "transport",
          "tools"
        ],
        "properties": {
          "name": {
            "type": "string",
            "example": "beancount"
          },
          "version": {
            "type": "string",
            "example": "1.0.0"
          },
          "transport": {
            "type": "string",
            "enum": [
              "streamable-http",
              "sse"
            ],
            "example": "streamable-http"
          },
          "endpoint": {
            "type": "string",
            "format": "uri",
            "example": "https://beancount.io/api-gateway/mcp"
          },
          "tools": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "description"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                },
                "inputSchema": {
                  "type": "object",
                  "description": "JSON Schema for tool inputs"
                }
              }
            }
          }
        }
      }
    }
  }
}