{
  "openapi": "3.0.3",
  "info": {
    "title": "Aggre accounts (control plane)",
    "version": "1.0-draft2",
    "description": "Console login, accounts, orgs, invitations, API keys. Cookie sessions; see [the accounts API](https://docs.aggre.org/api/accounts)\nfor the browser contract (credentials, Origin, X-Aggre-CSRF), the login redirect flow and the rules.\nEvery non-GET request needs the headers `X-Aggre-CSRF: 1` and (with a body) `Content-Type: application/json`.\n"
  },
  "servers": [
    {
      "url": "https://accounts.aggre.org"
    }
  ],
  "security": [
    {
      "sessionCookie": []
    }
  ],
  "tags": [
    {
      "name": "config"
    },
    {
      "name": "login"
    },
    {
      "name": "me"
    },
    {
      "name": "accounts"
    },
    {
      "name": "orgs"
    },
    {
      "name": "invitations"
    },
    {
      "name": "api-keys"
    }
  ],
  "paths": {
    "/login/{provider}": {
      "get": {
        "tags": [
          "login"
        ],
        "operationId": "loginStart",
        "summary": "Start a login (full-page navigation, answers 302 to the provider)",
        "security": [],
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "audience",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "app",
                "admin"
              ],
              "default": "app"
            }
          },
          {
            "name": "return_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uri"
            },
            "description": "Absolute URL on the audience origin; otherwise ignored."
          },
          {
            "name": "link",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            },
            "description": "Attach this provider to the signed-in user."
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the provider (or to {origin}/login?error=…)"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/login/callback/{provider}": {
      "get": {
        "tags": [
          "login"
        ],
        "operationId": "loginCallback",
        "summary": "OAuth redirect URI. Sets the session cookie and answers 302 to return_to or {origin}/login?error=<code>",
        "security": [],
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "code",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "error",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/config": {
      "get": {
        "tags": [
          "config"
        ],
        "operationId": "getConfig",
        "summary": "Public console configuration (login buttons, limits, defaults)",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Config"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "tags": [
          "me"
        ],
        "operationId": "getMe",
        "summary": "The signed-in user, identities, memberships and session",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Me"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "tags": [
          "me"
        ],
        "operationId": "deleteMe",
        "summary": "Delete the personal account (keys revoked, identities and sessions removed, handle stays reserved)",
        "parameters": [
          {
            "name": "confirm",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must equal the user's handle."
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/me/identities/{identity_id}": {
      "delete": {
        "tags": [
          "me"
        ],
        "operationId": "unlinkIdentity",
        "summary": "Unlink a login method (409 if it is the last one)",
        "parameters": [
          {
            "name": "identity_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Unlinked"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/auth/logout": {
      "post": {
        "tags": [
          "login"
        ],
        "operationId": "logout",
        "summary": "Revoke the current session and clear its cookie",
        "responses": {
          "204": {
            "description": "Logged out"
          }
        }
      }
    },
    "/v1/auth/logout-all": {
      "post": {
        "tags": [
          "login"
        ],
        "operationId": "logoutAll",
        "summary": "Revoke every session of the user (both audiences)",
        "responses": {
          "204": {
            "description": "Logged out everywhere"
          }
        }
      }
    },
    "/v1/handles/{handle}": {
      "get": {
        "tags": [
          "accounts"
        ],
        "operationId": "checkHandle",
        "summary": "Is this handle usable? (signed-in users only)",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "handle",
                        "available"
                      ],
                      "properties": {
                        "handle": {
                          "type": "string"
                        },
                        "available": {
                          "type": "boolean"
                        },
                        "reason": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "invalid",
                            "reserved",
                            "taken",
                            null
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/accounts/{handle}": {
      "get": {
        "tags": [
          "accounts"
        ],
        "operationId": "getAccount",
        "summary": "One account you can see (self, member, or staff)",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          }
        ],
        "responses": {
          "200": {
            "$ref": "#/components/responses/Account"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "patch": {
        "tags": [
          "accounts"
        ],
        "operationId": "updateAccount",
        "summary": "Change display_name and/or handle (user: self; org: owner). First rename free, then once per 30 days.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "display_name": {
                    "type": "string",
                    "maxLength": 80,
                    "nullable": true
                  },
                  "handle": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "$ref": "#/components/responses/Account"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/accounts/{handle}/usage": {
      "get": {
        "tags": [
          "accounts"
        ],
        "operationId": "getUsage",
        "summary": "Request counts per UTC day and per key over the last N days",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90,
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Usage"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/accounts/{handle}/audit": {
      "get": {
        "tags": [
          "accounts"
        ],
        "operationId": "listAudit",
        "summary": "Audit log of the account (owner/admin/staff), newest first",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "before",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            },
            "description": "Return entries with id < before."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "next_before"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AuditEntry"
                      }
                    },
                    "next_before": {
                      "type": "integer",
                      "format": "int64",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/accounts/{handle}/api-keys": {
      "get": {
        "tags": [
          "api-keys"
        ],
        "operationId": "listApiKeys",
        "summary": "Keys of the account, newest first, revoked ones included (max 200). Never contains secrets.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApiKey"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "tags": [
          "api-keys"
        ],
        "operationId": "createApiKey",
        "summary": "Create a key. The secret is in the response once and never again.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApiKey"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "secret"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ApiKey"
                    },
                    "secret": {
                      "type": "string",
                      "example": "aggre_sk_…"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/accounts/{handle}/api-keys/{key_id}": {
      "patch": {
        "tags": [
          "api-keys"
        ],
        "operationId": "updateApiKey",
        "summary": "Rename a key or replace its ip_allowlist",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          },
          {
            "$ref": "#/components/parameters/KeyId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  },
                  "ip_allowlist": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "CIDRs or bare addresses; [] = open."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/ApiKey"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "tags": [
          "api-keys"
        ],
        "operationId": "revokeApiKey",
        "summary": "Revoke a key (takes effect on the gateway within ~30 s)",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          },
          {
            "$ref": "#/components/parameters/KeyId"
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/orgs": {
      "post": {
        "tags": [
          "orgs"
        ],
        "operationId": "createOrg",
        "summary": "Create an org; the caller becomes its owner",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "handle"
                ],
                "properties": {
                  "handle": {
                    "type": "string"
                  },
                  "display_name": {
                    "type": "string",
                    "maxLength": 80
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "$ref": "#/components/responses/Account"
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/orgs/{handle}": {
      "delete": {
        "tags": [
          "orgs"
        ],
        "operationId": "deleteOrg",
        "summary": "Delete an org (owner). Keys are revoked, memberships removed, the handle stays reserved.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          },
          {
            "name": "confirm",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Must equal the org handle."
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/orgs/{handle}/members": {
      "get": {
        "tags": [
          "orgs"
        ],
        "operationId": "listMembers",
        "summary": "Members of the org (any member, or staff)",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Member"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/orgs/{handle}/members/{user_handle}": {
      "patch": {
        "tags": [
          "orgs"
        ],
        "operationId": "updateMember",
        "summary": "Change a member's role (owner). 409 if it would leave no owner.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          },
          {
            "name": "user_handle",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "role"
                ],
                "properties": {
                  "role": {
                    "$ref": "#/components/schemas/Role"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Member"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "tags": [
          "orgs"
        ],
        "operationId": "removeMember",
        "summary": "Remove a member, or leave (user_handle = yourself). 409 for the last owner.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          },
          {
            "name": "user_handle",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/orgs/{handle}/invitations": {
      "get": {
        "tags": [
          "invitations"
        ],
        "operationId": "listInvitations",
        "summary": "Pending invitations (owner/admin)",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Invitation"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "tags": [
          "invitations"
        ],
        "operationId": "createInvitation",
        "summary": "Invite an e-mail address. Re-inviting the same address replaces the pending invitation. No e-mail is sent; share the link.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "role": {
                    "$ref": "#/components/schemas/Role"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data",
                    "token"
                  ],
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/Invitation"
                    },
                    "token": {
                      "type": "string",
                      "description": "One-time secret; the console links to {app}/invitations/{token}."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/orgs/{handle}/invitations/{invitation_id}": {
      "delete": {
        "tags": [
          "invitations"
        ],
        "operationId": "revokeInvitation",
        "summary": "Revoke a pending invitation",
        "parameters": [
          {
            "$ref": "#/components/parameters/Handle"
          },
          {
            "name": "invitation_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/invitations/{token}": {
      "get": {
        "tags": [
          "invitations"
        ],
        "operationId": "previewInvitation",
        "summary": "What an invitation link is for (no session needed, so the page can ask the visitor to sign in first)",
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "org",
                        "role",
                        "email_hint",
                        "expires_at"
                      ],
                      "properties": {
                        "org": {
                          "type": "object",
                          "required": [
                            "handle"
                          ],
                          "properties": {
                            "handle": {
                              "type": "string"
                            },
                            "display_name": {
                              "type": "string",
                              "nullable": true
                            }
                          }
                        },
                        "role": {
                          "$ref": "#/components/schemas/Role"
                        },
                        "email_hint": {
                          "type": "string",
                          "example": "a***@example.com"
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/invitations/{token}/accept": {
      "post": {
        "tags": [
          "invitations"
        ],
        "operationId": "acceptInvitation",
        "summary": "Join the org. The caller's verified primary e-mail must equal the invited address (403 email_mismatch).",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Joined",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "data"
                  ],
                  "properties": {
                    "data": {
                      "type": "object",
                      "required": [
                        "org",
                        "role"
                      ],
                      "properties": {
                        "org": {
                          "$ref": "#/components/schemas/Account"
                        },
                        "role": {
                          "$ref": "#/components/schemas/Role"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "__Host-aggre_app",
        "description": "__Host-aggre_app for origin app, __Host-aggre_admin for origin admin. HttpOnly; the browser sends it."
      }
    },
    "parameters": {
      "Handle": {
        "name": "handle",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "KeyId": {
        "name": "key_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Error envelope. 401 unauthorized · 403 forbidden | origin_not_allowed | csrf_required | step_up_required |\naccount_disabled | email_mismatch | bootstrap_invalid | bootstrap_disabled · 404 not_found · 409 conflict | provider_read_only ·\n400 invalid_request · 422 validation_failed · 429 rate_limited · 503 store_unavailable | secrets_key_missing · 500 internal\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Account": {
        "description": "OK",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "data"
              ],
              "properties": {
                "data": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "request_id"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "unauthorized",
                  "forbidden",
                  "ip_not_allowed",
                  "not_found",
                  "invalid_request",
                  "rate_limited",
                  "usage_limit_exceeded",
                  "store_unavailable",
                  "internal",
                  "conflict",
                  "origin_not_allowed",
                  "csrf_required",
                  "step_up_required",
                  "account_disabled",
                  "email_mismatch",
                  "provider_read_only",
                  "validation_failed",
                  "secrets_key_missing",
                  "bootstrap_invalid",
                  "bootstrap_disabled",
                  "credentials_key_missing",
                  "taken_down",
                  "index_unavailable",
                  "retrieval_unavailable",
                  "corpus_not_migrated",
                  "terms_unread"
                ]
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              }
            }
          }
        }
      },
      "Role": {
        "type": "string",
        "enum": [
          "owner",
          "admin",
          "member"
        ]
      },
      "Config": {
        "type": "object",
        "required": [
          "providers",
          "key_defaults",
          "handle_rules",
          "invitation_ttl_days"
        ],
        "properties": {
          "providers": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "display_name",
                "protocol"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "example": "google"
                },
                "display_name": {
                  "type": "string"
                },
                "protocol": {
                  "type": "string",
                  "enum": [
                    "oidc",
                    "oauth2"
                  ]
                }
              }
            }
          },
          "key_defaults": {
            "type": "object",
            "required": [
              "usage_limit",
              "usage_period",
              "max_active_keys"
            ],
            "properties": {
              "usage_limit": {
                "type": "integer",
                "format": "int64"
              },
              "usage_period": {
                "type": "string",
                "enum": [
                  "day",
                  "month",
                  "total"
                ]
              },
              "max_active_keys": {
                "type": "integer"
              }
            }
          },
          "handle_rules": {
            "type": "object",
            "required": [
              "min_length",
              "max_length",
              "pattern"
            ],
            "properties": {
              "min_length": {
                "type": "integer"
              },
              "max_length": {
                "type": "integer"
              },
              "pattern": {
                "type": "string"
              }
            }
          },
          "invitation_ttl_days": {
            "type": "integer"
          }
        }
      },
      "Account": {
        "type": "object",
        "required": [
          "id",
          "handle",
          "kind",
          "display_name",
          "avatar_url",
          "created_at",
          "my_role"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "handle": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "user",
              "org"
            ]
          },
          "display_name": {
            "type": "string",
            "nullable": true
          },
          "avatar_url": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "my_role": {
            "type": "string",
            "nullable": true,
            "enum": [
              "owner",
              "admin",
              "member",
              "staff",
              null
            ],
            "description": "owner for your own personal account; staff = platform staff without membership."
          }
        }
      },
      "Me": {
        "type": "object",
        "required": [
          "user",
          "identities",
          "memberships",
          "is_platform_staff",
          "session"
        ],
        "properties": {
          "user": {
            "type": "object",
            "required": [
              "id",
              "handle",
              "display_name",
              "avatar_url",
              "primary_email",
              "created_at",
              "can_change_handle"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "handle": {
                "type": "string"
              },
              "display_name": {
                "type": "string",
                "nullable": true
              },
              "avatar_url": {
                "type": "string",
                "nullable": true
              },
              "primary_email": {
                "type": "string",
                "nullable": true,
                "description": "Verified e-mail or null."
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "can_change_handle": {
                "type": "boolean"
              },
              "handle_change_available_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "identities": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "provider",
                "provider_email",
                "email_verified",
                "linked_at"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "provider": {
                  "type": "string"
                },
                "provider_email": {
                  "type": "string",
                  "nullable": true
                },
                "email_verified": {
                  "type": "boolean"
                },
                "linked_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "memberships": {
            "type": "array",
            "description": "Orgs only, ordered by handle. The personal account is `user` (its handle is the account handle), never an element of this list.",
            "items": {
              "type": "object",
              "required": [
                "account",
                "role"
              ],
              "properties": {
                "account": {
                  "$ref": "#/components/schemas/Account"
                },
                "role": {
                  "$ref": "#/components/schemas/Role"
                }
              }
            }
          },
          "is_platform_staff": {
            "type": "boolean"
          },
          "session": {
            "type": "object",
            "required": [
              "audience",
              "expires_at",
              "authenticated_at"
            ],
            "properties": {
              "audience": {
                "type": "string",
                "enum": [
                  "app",
                  "admin"
                ]
              },
              "expires_at": {
                "type": "string",
                "format": "date-time"
              },
              "authenticated_at": {
                "type": "string",
                "format": "date-time"
              },
              "bootstrap": {
                "type": "boolean",
                "description": "true for a session created by the temporary bootstrap login ([the accounts API](https://docs.aggre.org/api/accounts) 4.2); the UI should warn. Absent or false otherwise."
              }
            }
          }
        }
      },
      "Member": {
        "type": "object",
        "required": [
          "user",
          "role",
          "joined_at"
        ],
        "properties": {
          "user": {
            "type": "object",
            "required": [
              "id",
              "handle",
              "display_name",
              "avatar_url"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "handle": {
                "type": "string"
              },
              "display_name": {
                "type": "string",
                "nullable": true
              },
              "avatar_url": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "role": {
            "$ref": "#/components/schemas/Role"
          },
          "joined_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Invitation": {
        "type": "object",
        "required": [
          "id",
          "email",
          "role",
          "invited_by",
          "created_at",
          "expires_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string"
          },
          "role": {
            "$ref": "#/components/schemas/Role"
          },
          "invited_by": {
            "type": "string",
            "nullable": true,
            "description": "Inviter's handle."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "required": [
          "id",
          "name",
          "prefix",
          "usage_limit",
          "usage_period",
          "rate_policy",
          "ip_allowlist",
          "created_at",
          "created_by",
          "expires_at",
          "last_used_at",
          "revoked_at",
          "usage"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "description": "8 non-secret characters after aggre_sk_."
          },
          "usage_limit": {
            "type": "integer",
            "format": "int64",
            "nullable": true,
            "description": "null = Unlimited."
          },
          "usage_period": {
            "type": "string",
            "enum": [
              "day",
              "month",
              "total"
            ]
          },
          "rate_policy": {
            "type": "string",
            "enum": [
              "default",
              "exempt"
            ]
          },
          "internal": {
            "type": "boolean",
            "description": "The key carries the `internal` scope that opens the data API's `/v1/internal/*` routes (per-row macro data). Absent in answers of servers that predate it; false for every key not issued as internal."
          },
          "ip_allowlist": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "string",
            "nullable": true,
            "description": "Handle of the creating user; null for CLI-created keys."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "usage": {
            "type": "object",
            "required": [
              "period_start",
              "count"
            ],
            "properties": {
              "period_start": {
                "type": "string",
                "format": "date"
              },
              "count": {
                "type": "integer",
                "format": "int64"
              }
            }
          }
        }
      },
      "CreateApiKey": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "usage_limit": {
            "type": "integer",
            "format": "int64",
            "nullable": true,
            "description": "Omit = server default. null (Unlimited) and values above the default: staff only."
          },
          "usage_period": {
            "type": "string",
            "enum": [
              "day",
              "month",
              "total"
            ]
          },
          "rate_policy": {
            "type": "string",
            "enum": [
              "default",
              "exempt"
            ],
            "description": "exempt: staff only."
          },
          "internal": {
            "type": "boolean",
            "default": false,
            "description": "true gives the key the `internal` scope (the data API's `/v1/internal/*` per-row routes). Platform staff on an admin session, freshly signed in; anyone else gets 403."
          },
          "ip_allowlist": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "Usage": {
        "type": "object",
        "required": [
          "from",
          "to",
          "total",
          "daily",
          "by_key"
        ],
        "properties": {
          "from": {
            "type": "string",
            "format": "date"
          },
          "to": {
            "type": "string",
            "format": "date"
          },
          "total": {
            "type": "integer",
            "format": "int64"
          },
          "daily": {
            "type": "array",
            "description": "One entry per UTC day in [from, to], zero-filled.",
            "items": {
              "type": "object",
              "required": [
                "date",
                "count"
              ],
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "count": {
                  "type": "integer",
                  "format": "int64"
                }
              }
            }
          },
          "by_key": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "key_id",
                "name",
                "prefix",
                "count"
              ],
              "properties": {
                "key_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "prefix": {
                  "type": "string"
                },
                "count": {
                  "type": "integer",
                  "format": "int64"
                }
              }
            }
          }
        }
      },
      "AuditEntry": {
        "type": "object",
        "required": [
          "id",
          "ts",
          "actor",
          "action",
          "detail"
        ],
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "ts": {
            "type": "string",
            "format": "date-time"
          },
          "actor": {
            "type": "string",
            "description": "user:<handle>, cli or system."
          },
          "action": {
            "type": "string",
            "example": "api_key.create"
          },
          "detail": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "AdminAuditEntry": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AuditEntry"
          },
          {
            "type": "object",
            "required": [
              "account",
              "api_key_id"
            ],
            "properties": {
              "account": {
                "type": "object",
                "nullable": true,
                "required": [
                  "id",
                  "handle"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "handle": {
                    "type": "string"
                  }
                }
              },
              "api_key_id": {
                "type": "string",
                "format": "uuid",
                "nullable": true
              }
            }
          }
        ]
      },
      "AdminSession": {
        "type": "object",
        "required": [
          "id",
          "audience",
          "created_at",
          "authenticated_at",
          "last_seen_at",
          "expires_at",
          "current"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "audience": {
            "type": "string",
            "enum": [
              "app",
              "admin"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "authenticated_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "current": {
            "type": "boolean",
            "description": "This is the session that made the request."
          }
        }
      },
      "AdminApiKey": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ApiKey"
          },
          {
            "type": "object",
            "required": [
              "account"
            ],
            "properties": {
              "account": {
                "type": "object",
                "required": [
                  "id",
                  "handle",
                  "kind",
                  "display_name"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "handle": {
                    "type": "string"
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "user",
                      "org"
                    ]
                  },
                  "display_name": {
                    "type": "string",
                    "nullable": true
                  }
                }
              }
            }
          }
        ]
      },
      "AdminOverview": {
        "type": "object",
        "required": [
          "generated_at",
          "accounts",
          "keys",
          "requests",
          "daily",
          "top_accounts"
        ],
        "properties": {
          "generated_at": {
            "type": "string",
            "format": "date-time"
          },
          "accounts": {
            "type": "object",
            "required": [
              "users",
              "orgs",
              "disabled"
            ],
            "properties": {
              "users": {
                "type": "integer",
                "format": "int64"
              },
              "orgs": {
                "type": "integer",
                "format": "int64"
              },
              "disabled": {
                "type": "integer",
                "format": "int64",
                "description": "Suspended accounts of either kind (not in users/orgs)."
              }
            }
          },
          "keys": {
            "type": "object",
            "required": [
              "active",
              "revoked",
              "unlimited",
              "exempt"
            ],
            "properties": {
              "active": {
                "type": "integer",
                "format": "int64",
                "description": "Not revoked and not expired."
              },
              "revoked": {
                "type": "integer",
                "format": "int64"
              },
              "unlimited": {
                "type": "integer",
                "format": "int64",
                "description": "Active keys with usage_limit null."
              },
              "exempt": {
                "type": "integer",
                "format": "int64",
                "description": "Active keys with rate_policy exempt."
              }
            }
          },
          "requests": {
            "type": "object",
            "required": [
              "today",
              "last_7d",
              "last_30d"
            ],
            "description": "Authenticated data-API requests (usage_events), UTC days; the windows include today.",
            "properties": {
              "today": {
                "type": "integer",
                "format": "int64"
              },
              "last_7d": {
                "type": "integer",
                "format": "int64"
              },
              "last_30d": {
                "type": "integer",
                "format": "int64"
              }
            }
          },
          "daily": {
            "type": "array",
            "description": "The last 30 UTC days, oldest first, zero-filled.",
            "items": {
              "type": "object",
              "required": [
                "date",
                "count"
              ],
              "properties": {
                "date": {
                  "type": "string",
                  "format": "date"
                },
                "count": {
                  "type": "integer",
                  "format": "int64"
                }
              }
            }
          },
          "top_accounts": {
            "type": "array",
            "description": "Up to 10 accounts by requests in the last 30 days.",
            "items": {
              "type": "object",
              "required": [
                "handle",
                "kind",
                "display_name",
                "count"
              ],
              "properties": {
                "handle": {
                  "type": "string"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "user",
                    "org"
                  ]
                },
                "display_name": {
                  "type": "string",
                  "nullable": true
                },
                "count": {
                  "type": "integer",
                  "format": "int64"
                }
              }
            }
          }
        }
      },
      "SyncRun": {
        "type": "object",
        "required": [
          "run_id",
          "worker",
          "dataset_id",
          "started_at",
          "finished_at",
          "unfinished",
          "status",
          "seen",
          "written",
          "unchanged",
          "removed",
          "remaining",
          "error_text"
        ],
        "properties": {
          "run_id": {
            "type": "string",
            "format": "uuid"
          },
          "worker": {
            "type": "string",
            "example": "legal-tw-moj"
          },
          "dataset_id": {
            "type": "string",
            "example": "legal.statutes.tw_moj"
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "unfinished": {
            "type": "boolean",
            "description": "finished_at is null: still running, or the worker died."
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "partial",
              "error"
            ]
          },
          "seen": {
            "type": "integer"
          },
          "written": {
            "type": "integer"
          },
          "unchanged": {
            "type": "integer"
          },
          "removed": {
            "type": "integer"
          },
          "remaining": {
            "type": "integer",
            "nullable": true,
            "description": "partial runs: how many are left (null = unknown, see error_text)."
          },
          "error_text": {
            "type": "string",
            "nullable": true,
            "description": "At most 4000 characters."
          }
        }
      },
      "LoginProvider": {
        "type": "object",
        "required": [
          "id",
          "display_name",
          "protocol",
          "kind",
          "issuer",
          "client_id",
          "has_secret",
          "scopes",
          "email_trust",
          "enabled",
          "source",
          "redirect_uri",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "google"
          },
          "display_name": {
            "type": "string"
          },
          "protocol": {
            "type": "string",
            "enum": [
              "oidc",
              "oauth2"
            ]
          },
          "kind": {
            "type": "string",
            "nullable": true,
            "enum": [
              "github",
              null
            ]
          },
          "issuer": {
            "type": "string",
            "nullable": true,
            "description": "OIDC issuer URL; null for GitHub."
          },
          "client_id": {
            "type": "string"
          },
          "has_secret": {
            "type": "boolean",
            "description": "Whether a client secret is stored. The secret itself is never returned."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "email_trust": {
            "type": "string",
            "enum": [
              "verified_claim_only",
              "never"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "source": {
            "type": "string",
            "enum": [
              "env",
              "db"
            ],
            "description": "env = AGGRE_ACCOUNTS_PROVIDERS, read-only."
          },
          "redirect_uri": {
            "type": "string",
            "example": "https://accounts.aggre.org/login/callback/google",
            "description": "Register exactly this at the provider."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "null for env providers."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "null for env providers."
          }
        }
      },
      "LoginProviderInput": {
        "type": "object",
        "required": [
          "protocol",
          "client_id"
        ],
        "additionalProperties": false,
        "properties": {
          "display_name": {
            "type": "string",
            "maxLength": 60
          },
          "protocol": {
            "type": "string",
            "enum": [
              "oidc",
              "oauth2"
            ]
          },
          "kind": {
            "type": "string",
            "nullable": true,
            "enum": [
              "github",
              null
            ],
            "description": "Required (github) for oauth2; null or absent for oidc."
          },
          "issuer": {
            "type": "string",
            "nullable": true,
            "description": "Required for oidc: https URL, not an IP literal or an internal name."
          },
          "client_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512
          },
          "client_secret": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "writeOnly": true,
            "description": "Required on create; omitted or null on update keeps the stored secret."
          },
          "scopes": {
            "type": "array",
            "maxItems": 16,
            "items": {
              "type": "string"
            },
            "description": "Default openid email profile (oidc) or read:user user:email (github)."
          },
          "email_trust": {
            "type": "string",
            "enum": [
              "verified_claim_only",
              "never"
            ],
            "default": "verified_claim_only"
          },
          "enabled": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "SourceCredential": {
        "type": "object",
        "required": [
          "name",
          "label",
          "family",
          "used_by",
          "secret",
          "help_url",
          "description",
          "has_value",
          "readable",
          "value",
          "updated_at",
          "updated_by",
          "last_used_at"
        ],
        "properties": {
          "name": {
            "type": "string",
            "example": "bea_api_key"
          },
          "label": {
            "type": "string"
          },
          "family": {
            "type": "string",
            "enum": [
              "market",
              "macro",
              "filings",
              "legal",
              "industry"
            ]
          },
          "used_by": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Worker names that read the slot (informational)."
          },
          "secret": {
            "type": "boolean",
            "description": "false: the value may be shown (sec_user_agent)."
          },
          "help_url": {
            "type": "string",
            "description": "Where to obtain the key."
          },
          "description": {
            "type": "string"
          },
          "has_value": {
            "type": "boolean"
          },
          "readable": {
            "type": "boolean",
            "description": "false when a value is stored but cannot be decrypted with the current key."
          },
          "value": {
            "type": "string",
            "nullable": true,
            "description": "Only for non-secret, readable slots; null otherwise. A secret value is never returned."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "updated_by": {
            "type": "string",
            "nullable": true,
            "example": "user:zou"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When a worker last read the value (at most one write per hour per slot)."
          }
        }
      },
      "SourceCredentialInput": {
        "type": "object",
        "required": [
          "value"
        ],
        "additionalProperties": false,
        "properties": {
          "value": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "writeOnly": true,
            "description": "Trimmed, then checked against the slot's validation rule ([the schema reference](https://docs.aggre.org/api/schemas))."
          }
        }
      },
      "Staff": {
        "type": "object",
        "required": [
          "user_id",
          "handle",
          "display_name",
          "primary_email",
          "kind",
          "created_at",
          "created_by",
          "last_login_at",
          "you"
        ],
        "properties": {
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "handle": {
            "type": "string"
          },
          "display_name": {
            "type": "string",
            "nullable": true
          },
          "primary_email": {
            "type": "string",
            "nullable": true,
            "description": "null for the bootstrap user."
          },
          "kind": {
            "type": "string",
            "enum": [
              "real",
              "bootstrap"
            ],
            "description": "real = has at least one login method."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "string",
            "example": "invite:user:zou",
            "description": "cli, bootstrap, invite:<inviter> or user:<handle>."
          },
          "last_login_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "you": {
            "type": "boolean",
            "description": "This is the calling user."
          }
        }
      },
      "StaffInvite": {
        "type": "object",
        "required": [
          "id",
          "email",
          "state",
          "invited_by",
          "created_at",
          "expires_at",
          "consumed_by"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "expired",
              "consumed"
            ]
          },
          "invited_by": {
            "type": "string",
            "example": "cli"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "consumed_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "User id; only set in the answer that consumed the invite at once."
          }
        }
      },
      "DatasetHealth": {
        "type": "object",
        "required": [
          "family",
          "dataset_id",
          "title",
          "worker",
          "deployed",
          "state",
          "expected_interval_hours",
          "stale_after_hours",
          "hours_since_ok",
          "instruments",
          "removed_instruments",
          "latest_run",
          "last_ok_run"
        ],
        "properties": {
          "family": {
            "type": "string",
            "enum": [
              "statutes",
              "name_list",
              "journal",
              "health"
            ],
            "description": "health = a health.* dataset read from health_db ([the accounts API](https://docs.aggre.org/api/accounts)); statutes = a legal.statutes.* dataset (counts are instruments); name_list = a legal.regulatory.* list (counts are entries, still under the names instruments / removed_instruments); journal = documents: the Federal Register (removed_instruments is always 0) and the Taiwan judgments (legal.judgments.tw_judicial, TWO rows, one per worker, told apart by `worker`; both carry the same corpus counts, removed_instruments = gone from the API or no longer listed) and the 大法官解釋 (legal.judgments.tw_interpretations, one row, worker legal-tw-interpretations, counted in its own documents)."
          },
          "dataset_id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "worker": {
            "type": "string"
          },
          "deployed": {
            "type": "boolean",
            "description": "false = listed in scripts/workers-not-deployed.txt."
          },
          "state": {
            "type": "string",
            "enum": [
              "healthy",
              "stale",
              "failing",
              "never_run",
              "not_deployed"
            ]
          },
          "expected_interval_hours": {
            "type": "integer",
            "nullable": true,
            "description": "How often the worker's CronJob is meant to run; null when not deployed."
          },
          "stale_after_hours": {
            "type": "integer",
            "nullable": true,
            "description": "Hours since the last ok run before the dataset is stale. statutes: expected_interval_hours x thresholds.stale_after_intervals. name_list: the list's own explicit window (OFAC 48, UN 72, OIG 240); journal: 96 (Federal Register), 48 (Taiwan judgments). null when not deployed."
          },
          "hours_since_ok": {
            "type": "number",
            "nullable": true,
            "description": "Age of the newest ok run, one decimal."
          },
          "instruments": {
            "type": "integer",
            "format": "int64",
            "description": "Instruments (statutes), entries (name_list) with removed_on null, or documents (journal)."
          },
          "removed_instruments": {
            "type": "integer",
            "format": "int64",
            "description": "Instruments or entries that were delisted (removed_on set)."
          },
          "unrenderable_documents": {
            "type": "integer",
            "format": "int64",
            "description": "Only on the Taiwan judgments rows: documents the API cannot render (a subset of instruments). Absent on every other row."
          },
          "index_coverage": {
            "type": "object",
            "description": "Only on the Taiwan judgments passage-index row (dataset_id legal.judgments.tw_judicial.index, worker judgments-indexer): what the indexer has recorded, not a probe of OpenSearch. instruments on this row = judgments with at least one passage in the best-covered generation. Absent on every other row.",
            "required": [
              "merits_documents",
              "generations",
              "outbox_pending",
              "outbox_given_up"
            ],
            "properties": {
              "merits_documents": {
                "type": "integer",
                "format": "int64",
                "description": "Merits judgments in judgments.document."
              },
              "outbox_pending": {
                "type": "integer",
                "format": "int64",
                "description": "Outbox rows waiting for the indexer, given-up rows included."
              },
              "outbox_given_up": {
                "type": "integer",
                "format": "int64",
                "description": "Pending rows the indexer stopped asking for after 3 attempts (judgments.index_attempt)."
              },
              "generations": {
                "type": "array",
                "description": "One entry per index generation that has any index_state row.",
                "items": {
                  "type": "object",
                  "required": [
                    "generation",
                    "index",
                    "entries",
                    "indexed_judgments",
                    "merits_without_passages",
                    "non_merits",
                    "passages",
                    "not_yet_indexed",
                    "coverage",
                    "newest_indexed_at"
                  ],
                  "properties": {
                    "generation": {
                      "type": "integer",
                      "format": "int64"
                    },
                    "index": {
                      "type": "string",
                      "description": "legal-judgments-v{generation}"
                    },
                    "entries": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Judgments the indexer has decided on, any outcome."
                    },
                    "indexed_judgments": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Entries with at least one passage."
                    },
                    "merits_without_passages": {
                      "type": "integer",
                      "format": "int64",
                      "description": "Merits judgments recorded with no passages (text erased, or nothing to cut)."
                    },
                    "non_merits": {
                      "type": "integer",
                      "format": "int64"
                    },
                    "passages": {
                      "type": "integer",
                      "format": "int64"
                    },
                    "not_yet_indexed": {
                      "type": "integer",
                      "format": "int64",
                      "description": "merits_documents minus the merits judgments this generation has decided, floored at 0."
                    },
                    "coverage": {
                      "type": "number",
                      "nullable": true,
                      "description": "Share of merits judgments decided, 0..1; null while there are none."
                    },
                    "newest_indexed_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "terms_read_on": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "Only on family health: the day a person recorded having read the publisher's terms (health.dataset.terms_read_on); null = nobody has read them. Without a reading or an accepted risk the worker makes no request. Absent on every other row."
          },
          "terms_state": {
            "type": "string",
            "enum": [
              "unread",
              "risk_accepted_unread",
              "read"
            ],
            "description": "Only on family health: `read` = terms read (worker collects, API serves); `risk_accepted_unread` = a person took the risk without reading (worker collects, API serves nothing); `unread` = neither (worker makes no request)."
          },
          "terms_risk_accepted_on": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "Only on family health: the day a person accepted the risk without reading the terms (health.dataset.terms_risk_accepted_on); null = none. Never a reading."
          },
          "fresh_clock": {
            "type": "string",
            "enum": [
              "ok",
              "ok_or_partial"
            ],
            "description": "Only on family health: which runs refresh the freshness clock. ok everywhere except trials (ok_or_partial: a bounded partial pass is progress)."
          },
          "hours_since_read": {
            "type": "number",
            "nullable": true,
            "description": "Only on family health: age of the newest ok or partial run, one decimal."
          },
          "instruments_approximate": {
            "type": "boolean",
            "description": "Only on family health: false = an exact count of a small table; true = a planner estimate of a large table, or (openFDA) the records read by the newest import of each imported partition. Never an exact count when true."
          },
          "partitions": {
            "type": "object",
            "description": "Only on the openFDA rows: this endpoint's files by status (fda.partition).",
            "required": [
              "imported",
              "pending",
              "failed",
              "skipped"
            ],
            "properties": {
              "imported": {
                "type": "integer",
                "format": "int64"
              },
              "pending": {
                "type": "integer",
                "format": "int64"
              },
              "failed": {
                "type": "integer",
                "format": "int64"
              },
              "skipped": {
                "type": "integer",
                "format": "int64"
              }
            }
          },
          "latest_run": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SyncRun"
              }
            ],
            "nullable": true
          },
          "last_ok_run": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SyncRun"
              }
            ],
            "nullable": true
          }
        }
      }
    }
  }
}