{
  "openapi": "3.1.0",
  "info": {
    "title": "PeriodGuard API",
    "version": "1.0.0",
    "summary": "Who changed your books, and when.",
    "description": "PeriodGuard keeps a full history of every change to your accounting records: who made it, when, and every value before and after. When anything changes in a month you have already closed, it emails you and your accountant. It only reads your books, never changes them.\n\n## Authentication\nSend an API key as `Authorization: Bearer <key>` (or `X-API-Key`). Keys are created on the account page (https://periodguard.com/app/api-keys). Signed-in browser sessions work too.\n\n## Metering and pricing\nEvery successful `changes` call records one `change_query` usage event. Accounts on a plan use their monthly allowance (Business: 2,000 results, $19/month; Practice: 10,000 results, $99/month; Practice 50: 50,000 results, $349/month); accounts on the free tier get 100 results per month. API and agent callers without a plan pay $0.01 per result.\nAgents should send `x-usage-source: agent` so their usage is attributed correctly; it does not change the price.\n\n## Rate limits\nPer key or IP, published in the `X-RateLimit-*` response headers. A 429 carries `Retry-After`.\n\nSupport: support@tenhatch.com. Machine-readable product summary: https://periodguard.com/.well-known/product.json",
    "contact": {
      "name": "PeriodGuard support",
      "email": "support@tenhatch.com",
      "url": "https://periodguard.com"
    },
    "termsOfService": "https://periodguard.com/legal/terms",
    "x-pricing": {
      "currency": "usd",
      "perResultCents": 1,
      "eventName": "change_query",
      "freeResultsPerMonth": 100,
      "plans": [
        {
          "planId": "business",
          "name": "Business",
          "monthly": "$19/month",
          "annual": "$190/year",
          "annualPerMonth": "$15.83/month",
          "savingsPercent": 17,
          "savings": "$38",
          "resultsPerMonth": 2000
        },
        {
          "planId": "practice",
          "name": "Practice",
          "monthly": "$99/month",
          "annual": "$990/year",
          "annualPerMonth": "$82.50/month",
          "savingsPercent": 17,
          "savings": "$198",
          "resultsPerMonth": 10000
        },
        {
          "planId": "practice-50",
          "name": "Practice 50",
          "monthly": "$349/month",
          "annual": "$3,490/year",
          "annualPerMonth": "$290.83/month",
          "savingsPercent": 17,
          "savings": "$698",
          "resultsPerMonth": 50000
        }
      ],
      "refundWindowDays": 14
    }
  },
  "servers": [
    {
      "url": "https://periodguard.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "apiKeyHeader": []
    }
  ],
  "tags": [
    {
      "name": "feature",
      "description": "Change history"
    },
    {
      "name": "account",
      "description": "Account, usage and API keys"
    },
    {
      "name": "meta",
      "description": "Documents about the API itself"
    }
  ],
  "paths": {
    "/api/v1/changes": {
      "post": {
        "tags": [
          "feature"
        ],
        "operationId": "changes",
        "summary": "Change history",
        "description": "Returns the changes PeriodGuard found in the businesses connected to your account: the record, what kind of change, when, who made it (from the accounting system’s own history), every field’s value before and after, and whether the record is dated in a closed period. Filter by business, record type, dates, person, closed periods only, or a word.\n\nInput: A JSON object, every field optional: `businessId`, `recordType` (invoice, credit-note, payment, overpayment, prepayment, bank-transaction, bank-transfer, manual-journal, contact, account), `from` and `to` (YYYY-MM-DD), `closedOnly` (true or false), `user`, `search`, and `limit` (1 to 500, default 100).\n\nOutput: A JSON object with `businesses` (id, name, status, closed-through date, last snapshot), `changes` (newest first; each with its business, record, change, times, record date before and after, closed period, who and the fields before and after), `total` and `truncated`.",
        "parameters": [
          {
            "name": "x-usage-source",
            "in": "header",
            "required": false,
            "description": "Set to `agent` when the caller is an AI agent or MCP client.",
            "schema": {
              "type": "string",
              "enum": [
                "agent",
                "api"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FeatureInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The result and how it was metered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnalyzeResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input (see `error.details`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key / session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Plan allowance exhausted and pay-as-you-go not available for this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Honour Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error; retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "x-price-per-result-cents": 1,
        "x-usage-event": "change_query"
      }
    },
    "/api/v1/usage": {
      "get": {
        "tags": [
          "account"
        ],
        "operationId": "getUsage",
        "summary": "Current-period usage meter",
        "responses": {
          "200": {
            "description": "Usage this calendar month.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageMeter"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key / session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Honour Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error; retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me": {
      "get": {
        "tags": [
          "account"
        ],
        "operationId": "getAccount",
        "summary": "The calling account, its plan and limits",
        "responses": {
          "200": {
            "description": "Account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key / session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Honour Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error; retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/keys": {
      "get": {
        "tags": [
          "account"
        ],
        "operationId": "listApiKeys",
        "summary": "List API keys (session only)",
        "security": [
          {
            "session": []
          }
        ],
        "responses": {
          "200": {
            "description": "Keys without secrets.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ApiKeySummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key / session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Honour Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error; retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "account"
        ],
        "operationId": "createApiKey",
        "summary": "Create an API key (session only)",
        "security": [
          {
            "session": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApiKeyInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new key; the secret is shown once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateApiKeyResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid input.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key / session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Key limit for the plan reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Honour Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error; retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/keys/{id}": {
      "delete": {
        "tags": [
          "account"
        ],
        "operationId": "revokeApiKey",
        "summary": "Revoke an API key (session only)",
        "security": [
          {
            "session": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked."
          },
          "401": {
            "description": "Missing or invalid API key / session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "No such key on this account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Honour Retry-After.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error; retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/openapi.json": {
      "get": {
        "tags": [
          "meta"
        ],
        "operationId": "getOpenApi",
        "summary": "This document",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1 document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key as a bearer token."
      },
      "apiKeyHeader": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      },
      "session": {
        "type": "apiKey",
        "in": "cookie",
        "name": "session",
        "description": "Browser session of a signed-in user."
      }
    },
    "schemas": {
      "FeatureInput": {
        "type": "object",
        "properties": {
          "businessId": {
            "description": "Only this business (an id from `businesses`).",
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "recordType": {
            "description": "Only this kind of record.",
            "type": "string",
            "enum": [
              "invoice",
              "credit-note",
              "payment",
              "overpayment",
              "prepayment",
              "bank-transaction",
              "bank-transfer",
              "manual-journal",
              "contact",
              "account"
            ]
          },
          "from": {
            "description": "Changes made on or after this day.",
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "to": {
            "description": "Changes made on or before this day.",
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "closedOnly": {
            "description": "Only changes to records dated in a closed period.",
            "type": "boolean"
          },
          "user": {
            "description": "Part of the name of the person who made the change.",
            "type": "string",
            "maxLength": 100
          },
          "search": {
            "description": "Part of the record's name, a field's name or a value before or after.",
            "type": "string",
            "maxLength": 100
          },
          "limit": {
            "description": "At most this many changes, newest first (default 100).",
            "type": "integer",
            "minimum": 1,
            "maximum": 500
          }
        },
        "description": "Filters for the change history; every field is optional."
      },
      "AnalyzeResponse": {
        "type": "object",
        "properties": {
          "result": {
            "type": "object",
            "properties": {
              "businesses": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "closedThrough": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "lastSnapshotAt": {
                      "anyOf": [
                        {
                          "type": "string",
                          "format": "date-time",
                          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "status",
                    "closedThrough",
                    "lastSnapshotAt"
                  ],
                  "additionalProperties": false
                }
              },
              "changes": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "business": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "id",
                        "name"
                      ],
                      "additionalProperties": false
                    },
                    "recordType": {
                      "type": "string",
                      "enum": [
                        "invoice",
                        "credit-note",
                        "payment",
                        "overpayment",
                        "prepayment",
                        "bank-transaction",
                        "bank-transfer",
                        "manual-journal",
                        "contact",
                        "account"
                      ]
                    },
                    "recordTypeLabel": {
                      "type": "string"
                    },
                    "record": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "label": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "id",
                        "label"
                      ],
                      "additionalProperties": false
                    },
                    "change": {
                      "type": "string",
                      "description": "Created, Edited, Voided, Deleted, Archived or Status changed."
                    },
                    "changedAt": {
                      "type": "string",
                      "format": "date-time",
                      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                      "description": "When the record was changed, as the accounting system records it."
                    },
                    "foundAt": {
                      "type": "string",
                      "format": "date-time",
                      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                      "description": "When PeriodGuard found the change."
                    },
                    "recordDate": {
                      "type": "object",
                      "properties": {
                        "before": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "after": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "before",
                        "after"
                      ],
                      "additionalProperties": false,
                      "description": "The record's own date (the one that puts it in a period) before and after the change."
                    },
                    "closedPeriod": {
                      "type": "boolean"
                    },
                    "closedThrough": {
                      "description": "The last closed day when the change was found.",
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "closedBy": {
                      "anyOf": [
                        {
                          "type": "string",
                          "enum": [
                            "end-of-year",
                            "period",
                            "closed-through"
                          ]
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "who": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "From the accounting system's own history of the record."
                    },
                    "whoSource": {
                      "type": "string",
                      "enum": [
                        "found",
                        "pending",
                        "none",
                        "unavailable"
                      ]
                    },
                    "fields": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "label": {
                            "type": "string"
                          },
                          "before": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "after": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        },
                        "required": [
                          "label",
                          "before",
                          "after"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "fieldsTruncated": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "id",
                    "business",
                    "recordType",
                    "recordTypeLabel",
                    "record",
                    "change",
                    "changedAt",
                    "foundAt",
                    "recordDate",
                    "closedPeriod",
                    "closedThrough",
                    "closedBy",
                    "who",
                    "whoSource",
                    "fields",
                    "fieldsTruncated"
                  ],
                  "additionalProperties": false,
                  "description": "One change to one record."
                }
              },
              "total": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              "truncated": {
                "type": "boolean"
              }
            },
            "required": [
              "businesses",
              "changes",
              "total",
              "truncated"
            ],
            "additionalProperties": false,
            "description": "The change history of the businesses on the account, newest first."
          },
          "usage": {
            "type": "object",
            "properties": {
              "source": {
                "type": "string",
                "enum": [
                  "human",
                  "agent",
                  "api"
                ],
                "description": "Which door a request came through: human (web app), agent (MCP / agent frameworks), api (plain API calls)."
              },
              "chargedCents": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              "remaining": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991,
                "description": "Results left in the current period after this call."
              }
            },
            "required": [
              "source",
              "chargedCents",
              "remaining"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "result",
          "usage"
        ],
        "additionalProperties": false,
        "description": "The feature result plus how it was metered."
      },
      "UsageMeter": {
        "type": "object",
        "properties": {
          "used": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "limit": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "remaining": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "percent": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "exhausted": {
            "type": "boolean"
          },
          "period": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
              },
              "end": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
              }
            },
            "required": [
              "start",
              "end"
            ],
            "additionalProperties": false
          },
          "bySource": {
            "type": "object",
            "properties": {
              "human": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              "agent": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              "api": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              }
            },
            "required": [
              "human",
              "agent",
              "api"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "used",
          "limit",
          "remaining",
          "percent",
          "exhausted",
          "period",
          "bySource"
        ],
        "additionalProperties": false,
        "description": "Results used in the current calendar month (UTC) against the plan limit, split by door."
      },
      "Account": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "email": {
            "type": "string",
            "format": "email",
            "pattern": "^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"
          },
          "plan": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name"
            ],
            "additionalProperties": false,
            "description": "The free tier has id \"free\"."
          },
          "limits": {
            "type": "object",
            "properties": {
              "resultsPerMonth": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              "apiKeys": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              "seats": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              "businesses": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              "historyDays": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              "alertRecipients": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              "snapshotIntervalMinutes": {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              }
            },
            "required": [
              "resultsPerMonth",
              "apiKeys",
              "seats",
              "businesses",
              "historyDays",
              "alertRecipients",
              "snapshotIntervalMinutes"
            ],
            "additionalProperties": false
          },
          "subscription": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string"
                  },
                  "interval": {
                    "type": "string",
                    "enum": [
                      "monthly",
                      "annual"
                    ]
                  },
                  "currentPeriodEnd": {
                    "anyOf": [
                      {
                        "type": "string",
                        "format": "date-time",
                        "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "cancelAtPeriodEnd": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "status",
                  "interval",
                  "currentPeriodEnd",
                  "cancelAtPeriodEnd"
                ],
                "additionalProperties": false
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "email",
          "plan",
          "limits",
          "subscription"
        ],
        "additionalProperties": false,
        "description": "The calling account."
      },
      "ApiKeySummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "description": "First characters of the key, for recognising it. The full key is only returned once, on creation."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
          },
          "lastUsedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
              },
              {
                "type": "null"
              }
            ]
          },
          "revokedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "name",
          "prefix",
          "createdAt",
          "lastUsedAt",
          "revokedAt"
        ],
        "additionalProperties": false,
        "description": "An API key without its secret."
      },
      "CreateApiKeyInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60,
            "description": "A label such as \"CI\" or \"Claude Desktop\"."
          }
        },
        "required": [
          "name"
        ],
        "description": "Input for creating an API key."
      },
      "CreateApiKeyResponse": {
        "type": "object",
        "properties": {
          "key": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "name": {
                "type": "string"
              },
              "prefix": {
                "type": "string",
                "description": "First characters of the key, for recognising it. The full key is only returned once, on creation."
              },
              "createdAt": {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
              },
              "lastUsedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "revokedAt": {
                "anyOf": [
                  {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$"
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            },
            "required": [
              "id",
              "name",
              "prefix",
              "createdAt",
              "lastUsedAt",
              "revokedAt"
            ],
            "additionalProperties": false,
            "description": "An API key without its secret."
          },
          "secret": {
            "type": "string",
            "description": "The full API key. Shown once; store it now."
          }
        },
        "required": [
          "key",
          "secret"
        ],
        "additionalProperties": false,
        "description": "A freshly created API key."
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "invalid_input",
                  "quota_exhausted",
                  "rate_limited",
                  "payment_required",
                  "not_open",
                  "internal"
                ]
              },
              "message": {
                "type": "string"
              },
              "details": {}
            },
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false,
        "description": "Error envelope returned with every non-2xx status."
      }
    }
  }
}