{
  "openapi": "3.1.0",
  "info": {
    "title": "RSX Access",
    "version": "v1",
    "description": "Controls who may install RSX applications and integrations, such as Roxy into a Discord server, and keeps the record of every decision."
  },
  "servers": [
    {
      "url": "https://api.rsx.group"
    }
  ],
  "components": {
    "schemas": {
      "AccessInstallPolicy": {
        "type": "object",
        "properties": {
          "requiredRole": {
            "type": "string",
            "enum": [
              "staff",
              "admin"
            ],
            "description": "Minimum RSX ID staff role. Downward-closed: an admin satisfies a staff requirement.",
            "example": "staff"
          },
          "requiredScopes": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]*(:[a-z0-9-*]+)*$",
              "description": "One RSX ID staff scope, named exactly. Wildcards are refused.",
              "example": "access:roxy:install"
            },
            "minItems": 1,
            "maxItems": 16,
            "description": "RSX ID staff scopes the account must hold, all of them. An RSX admin holds every scope.",
            "example": [
              "access:roxy:install"
            ]
          }
        },
        "required": [
          "requiredRole",
          "requiredScopes"
        ]
      },
      "AccessAdminDiscordView": {
        "type": "object",
        "properties": {
          "clientId": {
            "type": [
              "string",
              "null"
            ],
            "example": "1204583957623459881"
          },
          "permissions": {
            "type": [
              "string",
              "null"
            ],
            "example": "277025508352"
          },
          "secretName": {
            "type": "string",
            "example": "DISCORD_CLIENT_SECRET_ROXY"
          },
          "secretConfigured": {
            "type": "boolean",
            "example": true
          },
          "redirectUri": {
            "type": [
              "string",
              "null"
            ],
            "example": "https://access.rsx.group/install/callback"
          }
        },
        "required": [
          "clientId",
          "permissions",
          "secretName",
          "secretConfigured",
          "redirectUri"
        ]
      },
      "AccessAdminApplication": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 63,
            "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
            "example": "roxy"
          },
          "name": {
            "type": "string",
            "example": "Roxy"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "example": "The RSX Discord bot."
          },
          "environment": {
            "type": "string",
            "enum": [
              "production",
              "non-production"
            ],
            "example": "production"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the application accepts new installations. Existing ones are unaffected.",
            "example": true
          },
          "installationTypes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "discord_guild",
                "discord_user"
              ],
              "example": "discord_guild"
            },
            "example": [
              "discord_guild",
              "discord_user"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "installPolicy": {
            "$ref": "#/components/schemas/AccessInstallPolicy"
          },
          "discord": {
            "$ref": "#/components/schemas/AccessAdminDiscordView"
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "environment",
          "enabled",
          "installationTypes",
          "createdAt",
          "updatedAt",
          "installPolicy",
          "discord"
        ]
      },
      "Problem": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "example": "https://docs.rsx.group/errors/not-found"
          },
          "title": {
            "type": "string",
            "example": "Not found"
          },
          "status": {
            "type": "integer",
            "example": 404
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string",
            "example": "/access/v1/installations/get"
          },
          "requestId": {
            "type": "string"
          }
        },
        "required": [
          "type",
          "title",
          "status"
        ]
      },
      "AccessDiscordConfig": {
        "type": "object",
        "properties": {
          "clientId": {
            "type": "string",
            "pattern": "^[0-9]{17,20}$",
            "example": "1204583957623459881"
          },
          "permissions": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[0-9]{1,20}$",
            "default": null,
            "example": "277025508352"
          }
        },
        "required": [
          "clientId"
        ]
      },
      "AccessCreateApplication": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 63,
            "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
            "example": "roxy"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100,
            "example": "Roxy"
          },
          "description": {
            "type": "string",
            "maxLength": 500,
            "example": "The RSX Discord bot."
          },
          "environment": {
            "type": "string",
            "enum": [
              "production",
              "non-production"
            ],
            "example": "production"
          },
          "installationTypes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "discord_guild",
                "discord_user"
              ],
              "example": "discord_guild"
            },
            "minItems": 1
          },
          "installPolicy": {
            "$ref": "#/components/schemas/AccessInstallPolicy"
          },
          "discord": {
            "$ref": "#/components/schemas/AccessDiscordConfig"
          },
          "enabled": {
            "type": "boolean",
            "default": false
          }
        },
        "required": [
          "id",
          "name",
          "environment",
          "installationTypes",
          "installPolicy"
        ]
      },
      "AccessUpdateApplication": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 63,
            "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
            "example": "roxy"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500
          },
          "enabled": {
            "type": "boolean"
          },
          "installationTypes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "discord_guild",
                "discord_user"
              ],
              "example": "discord_guild"
            },
            "minItems": 1
          },
          "installPolicy": {
            "$ref": "#/components/schemas/AccessInstallPolicy"
          },
          "discord": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AccessDiscordConfig"
              },
              {
                "type": [
                  "object",
                  "null"
                ]
              }
            ]
          }
        },
        "required": [
          "id"
        ]
      },
      "AccessInstallation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "0198f3c4-7d2b-7e1f-8a2b-3c4d5e6f7a8b"
          },
          "applicationId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 63,
            "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
            "example": "roxy"
          },
          "identityId": {
            "type": "string",
            "format": "uuid",
            "description": "The RSX ID account that installed it.",
            "example": "0198f2b1-6c2a-7c9e-9d3a-1e2f3a4b5c6d"
          },
          "targetType": {
            "type": "string",
            "enum": [
              "discord_guild",
              "discord_user"
            ],
            "example": "discord_guild"
          },
          "targetId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9._:-]+$",
            "example": "1204583957623459881"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "revoked"
            ],
            "example": "active"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "revokedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "example": null
          },
          "revokedBy": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "owner",
              "operator"
            ],
            "example": null
          }
        },
        "required": [
          "id",
          "applicationId",
          "identityId",
          "targetType",
          "targetId",
          "status",
          "createdAt",
          "updatedAt",
          "revokedAt",
          "revokedBy"
        ]
      },
      "AccessPageMeta": {
        "type": "object",
        "properties": {
          "cursor": {
            "type": [
              "string",
              "null"
            ],
            "example": null
          }
        },
        "required": [
          "cursor"
        ]
      },
      "AccessRemoveInstallation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "0198f3c4-7d2b-7e1f-8a2b-3c4d5e6f7a8b"
          }
        },
        "required": [
          "id"
        ]
      },
      "AccessAuditEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "0198f3c4-7d2b-7e1f-8a2b-3c4d5e6f7a8b"
          },
          "action": {
            "type": "string",
            "enum": [
              "installation.authorize",
              "installation.add",
              "installation.remove",
              "application.add",
              "application.update"
            ],
            "example": "installation.add"
          },
          "decision": {
            "type": "string",
            "enum": [
              "allowed",
              "denied"
            ],
            "example": "denied"
          },
          "reason": {
            "type": "string",
            "enum": [
              "permitted",
              "revoked-by-owner",
              "revoked-by-operator",
              "registered",
              "updated",
              "application-not-found",
              "application-disabled",
              "application-not-configured",
              "insufficient-role",
              "insufficient-scope",
              "installation-not-supported",
              "installation-target-invalid",
              "installation-state-invalid",
              "installation-exchange-failed",
              "installation-not-found",
              "installation-revoked",
              "access-dependency-unavailable",
              "invalid-token",
              "account-suspended",
              "account-terminated"
            ],
            "example": "insufficient-scope"
          },
          "actorType": {
            "type": "string",
            "enum": [
              "account",
              "operator"
            ],
            "example": "account"
          },
          "actorId": {
            "type": "string",
            "example": "0198f2b1-6c2a-7c9e-9d3a-1e2f3a4b5c6d"
          },
          "applicationId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The application as requested. On a denial this may name one that does not exist, which is the point: a probe for unregistered applications is worth being able to see.",
            "example": "roxy"
          },
          "installationId": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "example": "0198f3c4-7d2b-7e1f-8a2b-3c4d5e6f7a8b"
          },
          "targetType": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "discord_guild",
              "discord_user"
            ],
            "example": "discord_guild"
          },
          "targetId": {
            "type": [
              "string",
              "null"
            ],
            "minLength": 1,
            "maxLength": 128,
            "pattern": "^[A-Za-z0-9._:-]+$",
            "example": "1204583957623459881"
          },
          "requestId": {
            "type": [
              "string",
              "null"
            ]
          },
          "correlationId": {
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {},
            "description": "Written by this service only, from fields it already validated, and never copied from a caller's body. On an application change, the fields before and after."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "action",
          "decision",
          "reason",
          "actorType",
          "actorId",
          "applicationId",
          "installationId",
          "targetType",
          "targetId",
          "requestId",
          "correlationId",
          "metadata",
          "createdAt"
        ]
      },
      "AccessEligibility": {
        "type": "object",
        "properties": {
          "canInstall": {
            "type": "boolean",
            "example": false
          },
          "reason": {
            "type": "string",
            "enum": [
              "permitted",
              "application-disabled",
              "application-not-configured",
              "insufficient-role",
              "insufficient-scope"
            ],
            "example": "insufficient-scope"
          },
          "requiredRole": {
            "type": "string",
            "enum": [
              "staff",
              "admin"
            ],
            "description": "Present when the reason is `insufficient-role`.",
            "example": "staff"
          },
          "missingScopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Present when the reason is `insufficient-scope`: the grants to ask an RSX admin for.",
            "example": [
              "access:roxy:install"
            ]
          }
        },
        "required": [
          "canInstall",
          "reason"
        ]
      },
      "AccessApplication": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 63,
            "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
            "example": "roxy"
          },
          "name": {
            "type": "string",
            "example": "Roxy"
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "example": "The RSX Discord bot."
          },
          "environment": {
            "type": "string",
            "enum": [
              "production",
              "non-production"
            ],
            "example": "production"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the application accepts new installations. Existing ones are unaffected.",
            "example": true
          },
          "installationTypes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "discord_guild",
                "discord_user"
              ],
              "example": "discord_guild"
            },
            "example": [
              "discord_guild",
              "discord_user"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "eligibility": {
            "$ref": "#/components/schemas/AccessEligibility"
          }
        },
        "required": [
          "id",
          "name",
          "description",
          "environment",
          "enabled",
          "installationTypes",
          "createdAt",
          "updatedAt",
          "eligibility"
        ]
      },
      "AccessInstallationAuthorization": {
        "type": "object",
        "properties": {
          "authorizeUrl": {
            "type": "string",
            "format": "uri",
            "description": "Discord's authorisation page for this application. Send the browser here; Discord returns it to the Access callback with `code` and `state`.",
            "example": "https://discord.com/oauth2/authorize?client_id=1204583957623459881&response_type=code&scope=bot+applications.commands"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "The `state` in the URL stops being accepted after this. Ten minutes from now."
          }
        },
        "required": [
          "authorizeUrl",
          "expiresAt"
        ]
      },
      "AccessAuthorizeInstallation": {
        "type": "object",
        "properties": {
          "applicationId": {
            "type": "string",
            "minLength": 1,
            "maxLength": 63,
            "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
            "example": "roxy"
          },
          "targetType": {
            "type": "string",
            "enum": [
              "discord_guild",
              "discord_user"
            ],
            "example": "discord_guild"
          }
        },
        "required": [
          "applicationId",
          "targetType"
        ]
      },
      "AccessCompleteInstallationMeta": {
        "type": "object",
        "properties": {
          "created": {
            "type": "boolean",
            "example": true
          }
        },
        "required": [
          "created"
        ]
      },
      "AccessCompleteInstallation": {
        "type": "object",
        "properties": {
          "state": {
            "type": "string",
            "minLength": 16,
            "maxLength": 256,
            "example": "p3Vh1tVb9o1r3yq0p7x0sQ"
          },
          "code": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "example": "NhhvTDYsFcdgNLnnLijcl7Ku7bEEeee"
          }
        },
        "required": [
          "state",
          "code"
        ]
      }
    },
    "parameters": {}
  },
  "paths": {
    "/access/v1/admin/applications/list": {
      "get": {
        "operationId": "adminListApplications",
        "summary": "List every registered application, with its install policy",
        "tags": [
          "admin"
        ],
        "responses": {
          "200": {
            "description": "Applications",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AccessAdminApplication"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or wrong platform admin token",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Not called over the dashboard's service binding, or no such record",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/access/v1/admin/applications/add": {
      "post": {
        "operationId": "adminAddApplication",
        "summary": "Register an application",
        "description": "Registers a new controlled application. It starts disabled unless `enabled` is set, because registering an application and opening it for installations are separate decisions.\n\n`discord` is the public half of its Discord setup. The client secret is never sent here: it is the Worker secret named in the response's `discord.secretName`, and until it is set the application answers `application-not-configured`.\n\nThe install policy must name at least one RSX ID staff scope. A policy with none would admit every member of RSX staff, and being staff is not meant to be enough on its own.",
        "tags": [
          "admin"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessCreateApplication"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Application registered",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AccessAdminApplication"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or wrong platform admin token",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Not called over the dashboard's service binding, or no such record",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The record's state does not permit this",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/access/v1/admin/applications/update": {
      "post": {
        "operationId": "adminUpdateApplication",
        "summary": "Change an application",
        "description": "Open or close it for installations, rename it, change the target types it supports, change its install policy, or set its Discord client id and permissions (`discord: null` removes them). Every change is written to the audit trail with the value before and after, so a policy that admitted someone can always be read back as it stood at the time.\n\nClosing an application or tightening its policy affects new installations only. Existing ones are revoked individually, with `admin/installations/remove`.",
        "tags": [
          "admin"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessUpdateApplication"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Application updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AccessAdminApplication"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or wrong platform admin token",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such record, or not one you can see",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/access/v1/admin/installations/list": {
      "get": {
        "operationId": "adminListInstallations",
        "summary": "List installations across every RSX ID",
        "description": "Newest first, narrowed by any combination of application, identity, target and status. `applicationId` + `targetType` + `targetId` answers \"who installed this into that\".",
        "tags": [
          "admin"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": ""
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "example": 20
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 63,
              "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
              "example": "roxy"
            },
            "required": false,
            "name": "applicationId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "0198f2b1-6c2a-7c9e-9d3a-1e2f3a4b5c6d"
            },
            "required": false,
            "name": "identityId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "discord_guild",
                "discord_user"
              ],
              "example": "discord_guild"
            },
            "required": false,
            "name": "targetType",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "pattern": "^[A-Za-z0-9._:-]+$",
              "example": "1204583957623459881"
            },
            "required": false,
            "name": "targetId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "revoked"
              ],
              "example": "active"
            },
            "required": false,
            "name": "status",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Installations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AccessInstallation"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/AccessPageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or wrong platform admin token",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Not called over the dashboard's service binding, or no such record",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/access/v1/admin/installations/remove": {
      "post": {
        "operationId": "adminRemoveInstallation",
        "summary": "Revoke any installation",
        "description": "The operator's revocation. Same effect as the owner revoking it, recorded as `revoked-by-operator` with the operator's name on the audit row. Like the owner's, it ends the Access relationship and does not remove the application from Discord.",
        "tags": [
          "admin"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessRemoveInstallation"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Installation revoked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AccessInstallation"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or wrong platform admin token",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such record, or not one you can see",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The record's state does not permit this",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/access/v1/admin/audit/list": {
      "get": {
        "operationId": "adminListAccessAudit",
        "summary": "Read the audit trail",
        "description": "Every decision Access has made, allowed and denied, newest first. Operator-only by design: it names who tried what, and it is not a feed for the people it describes.",
        "tags": [
          "admin"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": ""
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "example": 20
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 63,
              "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
              "example": "roxy"
            },
            "required": false,
            "name": "applicationId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 320
            },
            "required": false,
            "name": "actorId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "0198f3c4-7d2b-7e1f-8a2b-3c4d5e6f7a8b"
            },
            "required": false,
            "name": "installationId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "installation.authorize",
                "installation.add",
                "installation.remove",
                "application.add",
                "application.update"
              ],
              "example": "installation.add"
            },
            "required": false,
            "name": "action",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "allowed",
                "denied"
              ],
              "example": "denied"
            },
            "required": false,
            "name": "decision",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Audit events",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AccessAuditEvent"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/AccessPageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing or wrong platform admin token",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Not called over the dashboard's service binding, or no such record",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/access/v1/applications/list": {
      "get": {
        "operationId": "listApplications",
        "summary": "List the applications you can install or manage",
        "description": "Applications whose install policy your RSX ID satisfies, plus any you hold an active installation of, so an installation stays visible after the grant behind it is withdrawn. Each carries `eligibility`, your standing against it right now.\n\nNot paginated: the registry is small and curated by RSX, and it is filtered per caller.",
        "tags": [
          "applications"
        ],
        "responses": {
          "200": {
            "description": "Applications",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AccessApplication"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing credential, a platform API key, or a token not issued for RSX Access",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured, or the account is not permitted this operation",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/access/v1/applications/get": {
      "get": {
        "operationId": "getApplication",
        "summary": "Fetch one application and your standing against it",
        "description": "Any registered application, whether or not you may install it. This is what an install page reads to tell a person why they are refused: `eligibility` names the missing role or scopes, which is what an `installations/add` refusal would say. The policy itself is not returned.",
        "tags": [
          "applications"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 63,
              "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
              "example": "roxy"
            },
            "required": true,
            "name": "id",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Application",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AccessApplication"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing credential, a platform API key, or a token not issued for RSX Access",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured, or the account is not permitted this operation",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such record, or not one you can see",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/access/v1/installations/authorize": {
      "post": {
        "operationId": "authorizeInstallation",
        "summary": "Start an installation",
        "description": "Decides whether your RSX ID may install an application, and if it may, answers with the Discord URL to send the browser to. Checked in order: the application exists, it is open and set up for Discord, your RSX ID satisfies its install policy, and it supports the kind of installation asked for. A personal installation also needs a Discord account verified on your RSX ID.\n\nNothing is installed yet. Discord sends the browser back with a `code` and a `state`, which `installations/add` turns into the installation. The `state` is single-use, bound to your RSX ID, and good for ten minutes.\n\nAllowed and refused requests are both written to the audit trail.",
        "tags": [
          "installations"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessAuthorizeInstallation"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Where to send the browser",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AccessInstallationAuthorization"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing credential, a platform API key, or a token not issued for RSX Access",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured, or the account is not permitted this operation",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such record, or not one you can see",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Well-formed, but not a target this application can be installed into",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "A dependency could not provide a decision; nothing was changed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/access/v1/installations/add": {
      "post": {
        "operationId": "addInstallation",
        "summary": "Finish an installation",
        "description": "Takes the `code` and `state` Discord put on the callback, and turns them into the installation. In order: the `state` must be one this RSX ID started and not yet used; the application must still be open and your RSX ID must still satisfy its policy, because either can change while you are at Discord; then the code is exchanged with Discord. For a server installation, that exchange is the moment the application joins the server, so a refusal before it means the application is never added.\n\nWhat is recorded is what Discord reports was installed: the server named in the exchange, or for a personal installation the Discord account that authorised it, which must be the one verified on your RSX ID. The `guild_id` Discord also puts on the callback is not used.\n\nIf you already hold an active installation for that target, it is returned with `meta.created` false: Discord has re-added the application by then, and the existing approval covers it.\n\nAllowed and refused requests are both written to the audit trail. Through the public API `add` needs an `Idempotency-Key`, like every `add`; the code and state are single-use regardless.",
        "tags": [
          "installations"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessCompleteInstallation"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Installed, or already installed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AccessInstallation"
                    },
                    "meta": {
                      "$ref": "#/components/schemas/AccessCompleteInstallationMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing credential, a platform API key, or a token not issued for RSX Access",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured, or the account is not permitted this operation",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such record, or not one you can see",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Well-formed, but not a target this application can be installed into",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "502": {
            "description": "Discord refused to complete the installation; nothing was installed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "A dependency could not provide a decision; nothing was changed",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/access/v1/installations/get": {
      "get": {
        "operationId": "getInstallation",
        "summary": "Fetch one of your installations",
        "description": "Yours only. An installation that belongs to another RSX ID is `installation-not-found`, the same answer as one that does not exist.",
        "tags": [
          "installations"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "0198f3c4-7d2b-7e1f-8a2b-3c4d5e6f7a8b"
            },
            "required": true,
            "name": "id",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Installation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AccessInstallation"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing credential, a platform API key, or a token not issued for RSX Access",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured, or the account is not permitted this operation",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such record, or not one you can see",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/access/v1/installations/list": {
      "get": {
        "operationId": "listInstallations",
        "summary": "List your installations",
        "description": "Yours only, newest first, active and revoked alike unless `status` narrows it. Reading your own installations needs no grant: they are a record of what you did.",
        "tags": [
          "installations"
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "example": ""
            },
            "required": false,
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20,
              "example": 20
            },
            "required": false,
            "name": "limit",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 63,
              "pattern": "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$",
              "example": "roxy"
            },
            "required": false,
            "name": "applicationId",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "revoked"
              ],
              "example": "active"
            },
            "required": false,
            "name": "status",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Installations",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AccessInstallation"
                      }
                    },
                    "meta": {
                      "$ref": "#/components/schemas/AccessPageMeta"
                    }
                  },
                  "required": [
                    "data",
                    "meta"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing credential, a platform API key, or a token not issued for RSX Access",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured, or the account is not permitted this operation",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/access/v1/installations/remove": {
      "post": {
        "operationId": "removeInstallation",
        "summary": "Revoke one of your installations",
        "description": "Ends the Access relationship: the installation becomes `revoked` and stays on record. It does not remove the application from Discord, which is the integration's own business.\n\nNeeds no grant. Withdrawing something you installed only ever reduces access, so a person whose install scope has since been removed can still revoke what they installed.",
        "tags": [
          "installations"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessRemoveInstallation"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Installation revoked",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "$ref": "#/components/schemas/AccessInstallation"
                    }
                  },
                  "required": [
                    "data"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "The request did not match the endpoint's schema",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Missing credential, a platform API key, or a token not issued for RSX Access",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Sign-in is not configured, or the account is not permitted this operation",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "No such record, or not one you can see",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "409": {
            "description": "The record's state does not permit this",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {}
}
