{
  "openapi": "3.1.0",
  "info": {
    "title": "Userland API",
    "version": "v0",
    "description": "Control-plane API for publishing and operating Userland apps."
  },
  "servers": [
    {
      "url": "https://api.userland.fun"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    },
    "schemas": {
      "PublishAppRequest": {
        "title": "Publish app request",
        "type": "object",
        "required": [
          "app",
          "runtime",
          "files"
        ],
        "additionalProperties": false,
        "properties": {
          "app": {
            "$ref": "#/components/schemas/AppManifest"
          },
          "runtime": {
            "$ref": "#/components/schemas/RuntimeManifest"
          },
          "resources": {
            "$ref": "#/components/schemas/ResourceManifest"
          },
          "files": {
            "type": "array",
            "minItems": 1,
            "maxItems": 1000,
            "items": {
              "$ref": "#/components/schemas/ReleaseFileInput"
            }
          },
          "message": {
            "type": "string"
          },
          "provenance": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "AppManifest": {
        "type": "object",
        "required": [
          "name"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "summary": {
            "type": "string"
          },
          "visibility": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ],
            "default": "public"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]{0,63}$",
              "not": {
                "enum": [
                  "_userland",
                  "system",
                  "auth",
                  "session",
                  "sessions",
                  "secrets",
                  "runtime"
                ]
              }
            }
          }
        }
      },
      "RuntimeManifest": {
        "type": "object",
        "required": [
          "static_root"
        ],
        "additionalProperties": false,
        "properties": {
          "static_root": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "pattern": "^(?!/)(?!.*\\\\)(?!_userland(?:/|$))(?!.*(?:^|/)\\.\\.?(?:/|$))(?!.*//).+$"
          },
          "server_entry": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "pattern": "^(?!/)(?!.*\\\\)(?!_userland(?:/|$))(?!.*(?:^|/)\\.\\.?(?:/|$))(?!.*//).+$"
          },
          "fallback": {
            "type": "string",
            "enum": [
              "server",
              "index.html",
              "404"
            ],
            "default": "404"
          }
        }
      },
      "ResourceManifest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "auth": {
            "$ref": "#/components/schemas/AuthSpec"
          },
          "data": {
            "$ref": "#/components/schemas/DataSpec"
          },
          "files": {
            "$ref": "#/components/schemas/FilesSpec"
          },
          "secrets": {
            "$ref": "#/components/schemas/SecretSpec"
          },
          "jobs": {
            "type": "object",
            "propertyNames": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]{0,63}$",
              "not": {
                "enum": [
                  "_userland",
                  "system",
                  "auth",
                  "session",
                  "sessions",
                  "secrets",
                  "runtime"
                ]
              }
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/JobSpec"
            }
          },
          "webhooks": {
            "type": "object",
            "propertyNames": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]{0,63}$",
              "not": {
                "enum": [
                  "_userland",
                  "system",
                  "auth",
                  "session",
                  "sessions",
                  "secrets",
                  "runtime"
                ]
              }
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/WebhookSpec"
            }
          }
        }
      },
      "AuthSpec": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "none",
              "app_users"
            ],
            "default": "none"
          },
          "roles": {
            "type": "array",
            "uniqueItems": true,
            "items": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]{0,63}$",
              "not": {
                "enum": [
                  "system",
                  "userland",
                  "platform"
                ]
              }
            }
          },
          "public_signup": {
            "type": "boolean"
          },
          "email_verification": {
            "type": "boolean"
          }
        }
      },
      "DataSpec": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "collections": {
            "type": "object",
            "propertyNames": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]{0,63}$",
              "not": {
                "enum": [
                  "_userland",
                  "system",
                  "auth",
                  "session",
                  "sessions",
                  "secrets",
                  "runtime"
                ]
              }
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/DataCollectionSpec"
            }
          }
        }
      },
      "DataCollectionSpec": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "fields": {
            "type": "object",
            "propertyNames": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9_]{0,63}$",
              "not": {
                "enum": [
                  "id",
                  "created_at",
                  "updated_at",
                  "deleted_at",
                  "data"
                ]
              }
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/DataFieldSpec"
            }
          },
          "indexes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DataIndexSpec"
            }
          },
          "access": {
            "$ref": "#/components/schemas/DataAccessSpec"
          }
        }
      },
      "DataFieldSpec": {
        "oneOf": [
          {
            "type": "string",
            "enum": [
              "string",
              "integer",
              "number",
              "boolean",
              "datetime",
              "json",
              "richtext",
              "enum",
              "reference"
            ]
          },
          {
            "$ref": "#/components/schemas/DataFieldObjectSpec"
          }
        ]
      },
      "DataFieldObjectSpec": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "type",
              "values"
            ],
            "additionalProperties": false,
            "properties": {
              "type": {
                "const": "enum"
              },
              "values": {
                "type": "array",
                "minItems": 1,
                "uniqueItems": true,
                "items": {
                  "type": "string",
                  "minLength": 1
                }
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "collection"
            ],
            "additionalProperties": false,
            "properties": {
              "type": {
                "const": "reference"
              },
              "collection": {
                "type": "string",
                "pattern": "^[a-z][a-z0-9-]{0,63}$",
                "not": {
                  "enum": [
                    "_userland",
                    "system",
                    "auth",
                    "session",
                    "sessions",
                    "secrets",
                    "runtime"
                  ]
                }
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type"
            ],
            "additionalProperties": false,
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "string",
                  "integer",
                  "number",
                  "boolean",
                  "datetime",
                  "json",
                  "richtext"
                ]
              }
            }
          }
        ]
      },
      "DataIndexSpec": {
        "type": "object",
        "required": [
          "name",
          "fields"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9_]{0,63}$",
            "not": {
              "enum": [
                "id",
                "created_at",
                "updated_at",
                "deleted_at",
                "data"
              ]
            }
          },
          "fields": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9_]{0,63}$",
              "not": {
                "enum": [
                  "id",
                  "created_at",
                  "updated_at",
                  "deleted_at",
                  "data"
                ]
              }
            }
          },
          "unique": {
            "type": "boolean"
          }
        }
      },
      "DataAccessSpec": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "read": {
            "type": "string",
            "anyOf": [
              {
                "enum": [
                  "server_only",
                  "authenticated",
                  "owner",
                  "public"
                ]
              },
              {
                "pattern": "^role:[a-z][a-z0-9-]{0,63}$"
              }
            ]
          },
          "write": {
            "type": "string",
            "anyOf": [
              {
                "enum": [
                  "server_only",
                  "authenticated",
                  "owner",
                  "public"
                ]
              },
              {
                "pattern": "^role:[a-z][a-z0-9-]{0,63}$"
              }
            ]
          }
        }
      },
      "FilesSpec": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "stores": {
            "type": "object",
            "propertyNames": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]{0,63}$",
              "not": {
                "enum": [
                  "_userland",
                  "system",
                  "auth",
                  "session",
                  "sessions",
                  "secrets",
                  "runtime"
                ]
              }
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/FileStoreSpec"
            }
          }
        }
      },
      "FileStoreSpec": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "public": {
            "type": "boolean"
          },
          "max_file_size_bytes": {
            "type": "integer",
            "minimum": 1
          },
          "allowed_content_types": {
            "type": "array",
            "uniqueItems": true,
            "items": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "pattern": "^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+/[!#$%&'*+\\-.^_`|~0-9A-Za-z]+(?:\\s*;\\s*[!#$%&'*+\\-.^_`|~0-9A-Za-z]+=(?:\\\"[^\\\"]*\\\"|[!#$%&'*+\\-.^_`|~0-9A-Za-z]+))*$"
            }
          }
        }
      },
      "SecretSpec": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "required": {
            "type": "array",
            "uniqueItems": true,
            "items": {
              "type": "string",
              "pattern": "^(?!USERLAND_)(?!CF_)(?!CLOUDFLARE_)[A-Z][A-Z0-9_]{0,63}$"
            }
          }
        }
      },
      "JobSpec": {
        "oneOf": [
          {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "trigger": {
                "const": "manual",
                "default": "manual"
              },
              "max_attempts": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10,
                "default": 3
              }
            }
          },
          {
            "type": "object",
            "required": [
              "trigger",
              "schedule"
            ],
            "additionalProperties": false,
            "properties": {
              "trigger": {
                "const": "schedule"
              },
              "schedule": {
                "type": "string",
                "enum": [
                  "every_15_minutes",
                  "hourly",
                  "daily"
                ]
              },
              "max_attempts": {
                "type": "integer",
                "minimum": 1,
                "maximum": 10,
                "default": 3
              }
            }
          }
        ]
      },
      "WebhookSpec": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "provider",
              "deliver_to"
            ],
            "additionalProperties": false,
            "properties": {
              "provider": {
                "const": "none"
              },
              "secret": {
                "type": "string",
                "pattern": "^(?!USERLAND_)(?!CF_)(?!CLOUDFLARE_)[A-Z][A-Z0-9_]{0,63}$"
              },
              "deliver_to": {
                "const": "server"
              },
              "path": {
                "type": "string"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "provider",
              "secret",
              "deliver_to"
            ],
            "additionalProperties": false,
            "properties": {
              "provider": {
                "type": "string",
                "enum": [
                  "generic_hmac",
                  "github"
                ]
              },
              "secret": {
                "type": "string",
                "pattern": "^(?!USERLAND_)(?!CF_)(?!CLOUDFLARE_)[A-Z][A-Z0-9_]{0,63}$"
              },
              "deliver_to": {
                "const": "server"
              },
              "path": {
                "type": "string"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "provider",
              "deliver_to",
              "job"
            ],
            "additionalProperties": false,
            "properties": {
              "provider": {
                "type": "string",
                "enum": [
                  "none",
                  "generic_hmac",
                  "github"
                ]
              },
              "secret": {
                "type": "string",
                "pattern": "^(?!USERLAND_)(?!CF_)(?!CLOUDFLARE_)[A-Z][A-Z0-9_]{0,63}$"
              },
              "deliver_to": {
                "const": "job"
              },
              "job": {
                "type": "string",
                "pattern": "^[a-z][a-z0-9-]{0,63}$",
                "not": {
                  "enum": [
                    "_userland",
                    "system",
                    "auth",
                    "session",
                    "sessions",
                    "secrets",
                    "runtime"
                  ]
                }
              },
              "path": {
                "type": "string"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "provider",
              "deliver_to"
            ],
            "additionalProperties": false,
            "properties": {
              "provider": {
                "type": "string",
                "enum": [
                  "none",
                  "generic_hmac",
                  "github"
                ]
              },
              "secret": {
                "type": "string",
                "pattern": "^(?!USERLAND_)(?!CF_)(?!CLOUDFLARE_)[A-Z][A-Z0-9_]{0,63}$"
              },
              "deliver_to": {
                "type": "string",
                "pattern": "^job:[a-z][a-z0-9-]{0,63}$"
              },
              "path": {
                "type": "string"
              }
            }
          }
        ]
      },
      "ReleaseFileInput": {
        "type": "object",
        "required": [
          "path",
          "content_type",
          "content_base64"
        ],
        "additionalProperties": false,
        "properties": {
          "path": {
            "type": "string",
            "minLength": 1,
            "maxLength": 512,
            "pattern": "^(?!/)(?!.*\\\\)(?!_userland(?:/|$))(?!.*(?:^|/)\\.\\.?(?:/|$))(?!.*//).+$"
          },
          "content_type": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "pattern": "^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+/[!#$%&'*+\\-.^_`|~0-9A-Za-z]+(?:\\s*;\\s*[!#$%&'*+\\-.^_`|~0-9A-Za-z]+=(?:\\\"[^\\\"]*\\\"|[!#$%&'*+\\-.^_`|~0-9A-Za-z]+))*$"
          },
          "content_base64": {
            "type": "string",
            "contentEncoding": "base64"
          }
        }
      },
      "ActivationResult": {
        "type": "object",
        "required": [
          "status",
          "reasons",
          "missing_secrets",
          "previous_release_id"
        ],
        "additionalProperties": false,
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "live",
              "inactive",
              "pending_secrets",
              "requires_migration",
              "failed"
            ]
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "missing_secrets": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^(?!USERLAND_)(?!CF_)(?!CLOUDFLARE_)[A-Z][A-Z0-9_]{0,63}$"
            }
          },
          "previous_release_id": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^rel_"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "ApiError": {
        "type": "object",
        "required": [
          "error"
        ],
        "additionalProperties": false,
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false,
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "HealthResponse": {
        "type": "object",
        "required": [
          "status",
          "service"
        ],
        "properties": {
          "status": {
            "const": "ok"
          },
          "service": {
            "const": "userland-api"
          },
          "db": {
            "type": "string",
            "enum": [
              "ok",
              "unavailable"
            ]
          },
          "public_base_url": {
            "type": "string",
            "format": "uri"
          },
          "api_base_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "AccountCreateRequest": {
        "type": "object",
        "required": [
          "username",
          "password"
        ],
        "additionalProperties": false,
        "properties": {
          "username": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]{2,31}$"
          },
          "password": {
            "type": "string",
            "minLength": 1
          },
          "email": {
            "type": "string",
            "format": "email"
          }
        }
      },
      "AccountCreateResponse": {
        "type": "object",
        "required": [
          "username",
          "api_key",
          "warning"
        ],
        "additionalProperties": false,
        "properties": {
          "username": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]{2,31}$"
          },
          "api_key": {
            "type": "string",
            "pattern": "^ap_"
          },
          "warning": {
            "type": "string"
          }
        }
      },
      "TokenRequest": {
        "type": "object",
        "required": [
          "username",
          "password"
        ],
        "additionalProperties": false,
        "properties": {
          "username": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]{2,31}$"
          },
          "password": {
            "type": "string",
            "minLength": 1
          }
        }
      },
      "TokenResponse": {
        "type": "object",
        "required": [
          "api_key",
          "warning"
        ],
        "additionalProperties": false,
        "properties": {
          "api_key": {
            "type": "string",
            "pattern": "^ap_"
          },
          "warning": {
            "type": "string"
          }
        }
      },
      "PublishAppResponse": {
        "type": "object",
        "required": [
          "status",
          "app_id",
          "release_id",
          "origin",
          "previous_release_id",
          "activation"
        ],
        "additionalProperties": false,
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "published",
              "stored"
            ]
          },
          "app_id": {
            "type": "string",
            "pattern": "^[a-z0-9]{12,24}$"
          },
          "release_id": {
            "type": "string",
            "pattern": "^rel_"
          },
          "origin": {
            "type": "string",
            "format": "uri"
          },
          "previous_release_id": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^rel_"
              },
              {
                "type": "null"
              }
            ]
          },
          "activation": {
            "$ref": "#/components/schemas/ActivationResult"
          }
        }
      },
      "AppSummary": {
        "type": "object",
        "required": [
          "app_id",
          "name",
          "visibility",
          "origin",
          "created_at",
          "updated_at",
          "live_release_id"
        ],
        "properties": {
          "app_id": {
            "type": "string",
            "pattern": "^[a-z0-9]{12,24}$"
          },
          "name": {
            "type": "string"
          },
          "summary": {
            "type": [
              "string",
              "null"
            ]
          },
          "visibility": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ]
          },
          "origin": {
            "type": "string",
            "format": "uri"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "deleted_at": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "live_release_id": {
            "anyOf": [
              {
                "type": "string",
                "pattern": "^rel_"
              },
              {
                "type": "null"
              }
            ]
          },
          "username": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]{2,31}$"
          }
        }
      },
      "ReleaseSummary": {
        "type": "object",
        "required": [
          "release_id",
          "message",
          "created_at",
          "activation_status",
          "activation_error",
          "is_live",
          "created_by"
        ],
        "properties": {
          "release_id": {
            "type": "string",
            "pattern": "^rel_"
          },
          "message": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "activation_status": {
            "type": "string",
            "enum": [
              "live",
              "inactive",
              "pending_secrets",
              "requires_migration",
              "failed"
            ]
          },
          "activation_error": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "is_live": {
            "type": "boolean"
          },
          "created_by": {
            "type": "object",
            "properties": {
              "api_key_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "key_prefix": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      },
      "ResourceState": {
        "type": "object",
        "required": [
          "resource_type",
          "resource_name",
          "spec"
        ],
        "properties": {
          "resource_type": {
            "type": "string",
            "enum": [
              "auth",
              "data",
              "files",
              "secrets",
              "jobs",
              "webhooks"
            ]
          },
          "resource_name": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9-]{0,63}$",
            "not": {
              "enum": [
                "_userland",
                "system",
                "auth",
                "session",
                "sessions",
                "secrets",
                "runtime"
              ]
            }
          },
          "spec": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "EventSummary": {
        "type": "object",
        "required": [
          "app_event_id",
          "type",
          "event_type",
          "severity",
          "level",
          "message",
          "metadata",
          "created_at"
        ],
        "properties": {
          "app_event_id": {
            "type": "string"
          },
          "release_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "app_user_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "api_key_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "type": {
            "type": "string"
          },
          "event_type": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "debug",
              "info",
              "warn",
              "error"
            ]
          },
          "level": {
            "type": "string",
            "enum": [
              "debug",
              "info",
              "warn",
              "error"
            ]
          },
          "message": {
            "type": "string"
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SecretSummary": {
        "type": "object",
        "required": [
          "name",
          "present"
        ],
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^(?!USERLAND_)(?!CF_)(?!CLOUDFLARE_)[A-Z][A-Z0-9_]{0,63}$"
          },
          "present": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AdminInviteRequest": {
        "type": "object",
        "required": [
          "email"
        ],
        "additionalProperties": false,
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]{0,63}$",
              "not": {
                "enum": [
                  "system",
                  "userland",
                  "platform"
                ]
              }
            }
          },
          "expires_in_seconds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2592000
          }
        }
      },
      "AdminInviteResponse": {
        "type": "object",
        "required": [
          "invite_id",
          "email",
          "roles",
          "expires_at",
          "invite_url"
        ],
        "properties": {
          "invite_id": {
            "type": "string",
            "pattern": "^inv_"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]{0,63}$",
              "not": {
                "enum": [
                  "system",
                  "userland",
                  "platform"
                ]
              }
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "invite_url": {
            "type": "string",
            "format": "uri"
          }
        }
      }
    },
    "requestBodies": {
      "PublishAppRequest": {
        "required": true,
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/PublishAppRequest"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/healthz": {
      "get": {
        "summary": "Health check",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v0/accounts": {
      "post": {
        "summary": "Create account and first API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccountCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Account created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountCreateResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/v0/auth/token": {
      "post": {
        "summary": "Issue a new API key",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TokenRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token issued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/v0/apps": {
      "get": {
        "summary": "List owner apps",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Apps",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "apps"
                  ],
                  "properties": {
                    "apps": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AppSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Create app and release",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/PublishAppRequest"
        },
        "responses": {
          "200": {
            "description": "Published or stored",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublishAppResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "413": {
            "description": "Bundle too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/v0/apps/{app_id}": {
      "get": {
        "summary": "Read app metadata",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "app_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "App",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppSummary"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Publish another release",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "app_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "$ref": "#/components/requestBodies/PublishAppRequest"
        },
        "responses": {
          "200": {
            "description": "Published or stored",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PublishAppResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "413": {
            "description": "Bundle too large",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Tombstone app and remove live pointer",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "app_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Unpublished",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "app_id",
                    "status",
                    "deleted_at"
                  ],
                  "properties": {
                    "app_id": {
                      "type": "string",
                      "pattern": "^[a-z0-9]{12,24}$"
                    },
                    "status": {
                      "const": "unpublished"
                    },
                    "deleted_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/v0/apps/{app_id}/releases": {
      "get": {
        "summary": "List releases",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "app_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Releases",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "app_id",
                    "releases"
                  ],
                  "properties": {
                    "app_id": {
                      "type": "string",
                      "pattern": "^[a-z0-9]{12,24}$"
                    },
                    "releases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReleaseSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/v0/apps/{app_id}/rollback": {
      "post": {
        "summary": "Rollback live pointer",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "app_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "release_id"
                ],
                "additionalProperties": false,
                "properties": {
                  "release_id": {
                    "type": "string",
                    "pattern": "^rel_"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rolled back",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "app_id",
                    "release_id",
                    "previous_release_id",
                    "origin",
                    "status"
                  ],
                  "properties": {
                    "app_id": {
                      "type": "string",
                      "pattern": "^[a-z0-9]{12,24}$"
                    },
                    "release_id": {
                      "type": "string",
                      "pattern": "^rel_"
                    },
                    "previous_release_id": {
                      "anyOf": [
                        {
                          "type": "string",
                          "pattern": "^rel_"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "origin": {
                      "type": "string",
                      "format": "uri"
                    },
                    "status": {
                      "const": "rolled_back"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/v0/apps/{app_id}/resources": {
      "get": {
        "summary": "List active resource view",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "app_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Resources",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "app_id",
                    "resources"
                  ],
                  "properties": {
                    "app_id": {
                      "type": "string",
                      "pattern": "^[a-z0-9]{12,24}$"
                    },
                    "resources": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ResourceState"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/v0/apps/{app_id}/events": {
      "get": {
        "summary": "List app events",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "app_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "severity",
            "in": "query",
            "required": false,
            "schema": {
              "enum": [
                "debug",
                "info",
                "warn",
                "error"
              ]
            }
          },
          {
            "name": "release_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Events",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "app_id",
                    "events",
                    "cursor"
                  ],
                  "properties": {
                    "app_id": {
                      "type": "string",
                      "pattern": "^[a-z0-9]{12,24}$"
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EventSummary"
                      }
                    },
                    "cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/v0/apps/{app_id}/secrets": {
      "get": {
        "summary": "List present secret names",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "app_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Secrets",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "app_id",
                    "secrets"
                  ],
                  "properties": {
                    "app_id": {
                      "type": "string",
                      "pattern": "^[a-z0-9]{12,24}$"
                    },
                    "secrets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/v0/apps/{app_id}/secrets/{name}": {
      "put": {
        "summary": "Set secret value",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "app_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "value"
                ],
                "additionalProperties": false,
                "properties": {
                  "value": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Secret set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecretSummary"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete secret value",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "app_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Secret deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "name",
                    "present"
                  ],
                  "properties": {
                    "name": {
                      "type": "string",
                      "pattern": "^(?!USERLAND_)(?!CF_)(?!CLOUDFLARE_)[A-Z][A-Z0-9_]{0,63}$"
                    },
                    "present": {
                      "const": false
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/v0/apps/{app_id}/admin-invites": {
      "post": {
        "summary": "Create app-user invite",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "app_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AdminInviteRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invite",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminInviteResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "409": {
            "description": "Conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    }
  }
}
