{
  "openapi": "3.1.0",
  "info": {
    "title": "Embarko API",
    "version": "2026-10-31",
    "description": "Deploy and manage apps on Embarko. Guide: https://embarko.ai/docs. Retry rules: never retry 4xx except 429 (slowly); retry 5xx once, checking status before re-sending a deploy."
  },
  "servers": [
    {
      "url": "https://ship.embarko.ai"
    }
  ],
  "components": {
    "securitySchemes": {
      "deployToken": {
        "type": "http",
        "scheme": "bearer",
        "description": "Deploy token. Request one with POST /api/public/deploy-tokens/request."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "code"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "next": {
            "type": "string"
          },
          "links": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "App": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "service",
              "static"
            ]
          },
          "claimed": {
            "type": "boolean"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "Deletion time while unclaimed."
          }
        }
      },
      "DeployReply": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "status": {
            "$ref": "#/components/schemas/Status"
          },
          "app": {
            "$ref": "#/components/schemas/App"
          },
          "next": {
            "type": "string",
            "description": "The one thing to do now."
          },
          "schedules": {
            "$ref": "#/components/schemas/DeploySchedules"
          },
          "warnings": {
            "type": "array",
            "description": "Problems that do not stop the deploy but should be fixed before telling the user it is done.",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string",
                  "enum": [
                    "in_process_timer",
                    "schedules_unchanged",
                    "schedules_on_static_site",
                    "embarko_json_not_at_top",
                    "embarko_json_misnamed",
                    "embarko_json_unknown_keys"
                  ],
                  "description": "in_process_timer: the code uses a timer library whose jobs are not guaranteed to run on Embarko; list them under schedules in embarko.json. schedules_unchanged: the app has schedules and the upload declares none, so they were kept. schedules_on_static_site: a static site cannot run schedules; not applied. embarko_json_not_at_top: found in a subfolder; only the top folder is read. embarko_json_misnamed: a near-miss name (.embarko, embarko.yaml...); only embarko.json is read. embarko_json_unknown_keys: keys Embarko does not use were ignored."
                },
                "libraries": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "expressions": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "cron": {
                        "type": "string"
                      },
                      "file": {
                        "type": "string"
                      }
                    }
                  }
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          },
          "links": {
            "type": "object",
            "properties": {
              "runSchedules": {
                "type": "string",
                "description": "POST here once the app is live to run every schedule once."
              }
            },
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "StatusReply": {
        "allOf": [
          {
            "$ref": "#/components/schemas/DeployReply"
          }
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Cause, when failed."
          }
        }
      },
      "Status": {
        "type": "string",
        "enum": [
          "building",
          "publishing",
          "starting",
          "live",
          "failed",
          "crashed",
          "not_deployed",
          "unknown"
        ]
      },
      "File": {
        "type": "object",
        "required": [
          "path",
          "content"
        ],
        "properties": {
          "path": {
            "type": "string",
            "description": "Relative to the app root."
          },
          "content": {
            "type": "string"
          },
          "encoding": {
            "type": "string",
            "enum": [
              "utf8",
              "base64"
            ],
            "default": "utf8"
          }
        }
      },
      "EnvVar": {
        "type": "object",
        "required": [
          "key",
          "value"
        ],
        "properties": {
          "key": {
            "type": "string",
            "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"
          },
          "value": {
            "type": "string"
          }
        }
      },
      "Accepted": {
        "type": "object",
        "properties": {
          "accepted": {
            "type": "boolean"
          },
          "deploymentId": {
            "type": "string"
          },
          "statusUrl": {
            "type": "string"
          },
          "terminal": {
            "type": "boolean"
          }
        }
      },
      "Run": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "trigger": {
            "type": "string",
            "enum": [
              "schedule",
              "manual"
            ]
          },
          "outcome": {
            "type": "string",
            "enum": [
              "running",
              "ok",
              "failed",
              "skipped"
            ]
          },
          "terminal": {
            "type": "boolean",
            "description": "False only while running."
          },
          "httpStatus": {
            "type": [
              "integer",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "responseExcerpt": {
            "type": [
              "string",
              "null"
            ],
            "description": "First 2KB of the app's reply."
          },
          "startedAt": {
            "type": "string",
            "format": "date-time"
          },
          "finishedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "durationMs": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "AlertChannel": {
        "type": "object",
        "description": "A place the app's alerts go besides the creator's email. A webhook or M91 URL is never returned: label is its host or the M91 channel's name.",
        "properties": {
          "id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "email",
              "webhook",
              "m91"
            ]
          },
          "label": {
            "type": "string"
          },
          "format": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "json",
              "slack",
              "discord",
              null
            ],
            "description": "For a webhook: how messages are sent."
          },
          "enabled": {
            "type": "boolean",
            "description": "false after 10 failed deliveries in a row, or when turned off."
          },
          "disabledReason": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastDeliveryAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastError": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AlertSettings": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Alerts on for this app (on by default)."
          },
          "warnings": {
            "type": "boolean",
            "description": "Also out-of-memory and failed-start notices."
          },
          "mutedUntil": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "owner": {
            "type": "object",
            "properties": {
              "email": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The app's creator, always told."
              }
            }
          },
          "channels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AlertChannel"
            }
          },
          "canEdit": {
            "type": "boolean",
            "description": "Whether this token may change alerts."
          }
        }
      },
      "Incident": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "down",
              "crash_loop",
              "oom",
              "wake_failed"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "resolved"
            ],
            "description": "open: happening now."
          },
          "cause": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "app",
              "platform",
              null
            ],
            "description": "app: the app's own code. platform: Embarko's side."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "openedAt": {
            "type": "string",
            "format": "date-time"
          },
          "resolvedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "resolution": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "recovered",
              "stopped",
              "expired",
              "notice",
              null
            ]
          },
          "crashCount": {
            "type": "integer"
          },
          "exitCode": {
            "type": [
              "integer",
              "null"
            ]
          },
          "oomKilled": {
            "type": "boolean"
          },
          "ackedBy": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who answered \"I'll handle it\" on a phone alert."
          },
          "ackedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Schedule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "cron": {
            "type": "string",
            "description": "Five fields, stored expanded (@daily becomes 0 0 * * *)."
          },
          "timezone": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "description": "The address that is called."
          },
          "enabled": {
            "type": "boolean"
          },
          "nextRunAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Null while paused."
          },
          "failing": {
            "type": "boolean"
          },
          "consecutiveFailures": {
            "type": "integer"
          },
          "alertWebhook": {
            "type": "object",
            "properties": {
              "set": {
                "type": "boolean"
              },
              "host": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "description": "The URL itself is never returned."
          },
          "lastRun": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Run"
              },
              {
                "type": "null"
              }
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "links": {
            "type": "object",
            "properties": {
              "runNow": {
                "type": "string",
                "description": "POST to run it once now."
              },
              "runs": {
                "type": "string",
                "description": "GET for past runs."
              }
            }
          }
        }
      },
      "ScheduleInput": {
        "type": "object",
        "properties": {
          "cron": {
            "type": "string",
            "maxLength": 100,
            "description": "minute hour day-of-month month day-of-week, or @hourly, @daily, @weekly, @monthly, @yearly. At most once every 5 minutes."
          },
          "path": {
            "type": "string",
            "maxLength": 512,
            "description": "Path on the app, starting with /. Called with GET."
          },
          "timezone": {
            "type": "string",
            "default": "UTC",
            "description": "IANA name."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 100
          },
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "alertWebhookUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri",
            "description": "Public https:// URL that gets a JSON POST on schedule.failed and schedule.recovered."
          }
        }
      },
      "DeploySchedules": {
        "type": "object",
        "description": "What happens to the schedules listed in this upload's embarko.json.",
        "properties": {
          "declared": {
            "type": "integer",
            "description": "How many embarko.json lists."
          },
          "appliesWhen": {
            "type": "string",
            "enum": [
              "live"
            ],
            "description": "They take effect when this deploy goes live."
          },
          "applied": {
            "type": "boolean",
            "description": "false when they will not be applied, e.g. a temporary app."
          },
          "reason": {
            "type": "string",
            "description": "Why they will not be applied."
          }
        }
      },
      "FeedbackType": {
        "type": "string",
        "enum": [
          "platform_bug",
          "missing_capability",
          "docs_issue",
          "unexpected_behavior",
          "other"
        ]
      }
    }
  },
  "paths": {
    "/apps": {
      "post": {
        "operationId": "deployApp",
        "summary": "Deploy or redeploy an app",
        "description": "Without a token the app is temporary (deleted at app.expiresAt unless claimed). With a token it is owned, or claimed if it was temporary. An optional embarko.json in the app's top folder (in the .tar.gz, or as one of the JSON files) holds settings; today only schedules, the app's full set of timed calls (matched by path, [] removes all, file or key absent leaves them unchanged). Schema: https://embarko.ai/schema/embarko.json. Guide: https://embarko.ai/docs/embarko-json. Checked before the build; schedules apply only when the deploy succeeds, and not for a temporary app. Embarko creates a CRON_SECRET env var and sends it as Authorization: Bearer on each scheduled call. Never put secrets in embarko.json.",
        "security": [
          {
            "deployToken": []
          },
          {}
        ],
        "parameters": [
          {
            "name": "X-App-Name",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "Required for multipart. JSON bodies may use appName instead."
          },
          {
            "name": "X-Agent-Name",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "description": "Your agent name, e.g. codex. Recorded in deploy history."
          },
          {
            "name": "X-App-Version",
            "in": "header",
            "schema": {
              "type": "string",
              "pattern": "^[a-zA-Z0-9._-]+$"
            }
          },
          {
            "name": "X-App-Type",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "description": "What the app is for, e.g. portfolio."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "source"
                ],
                "properties": {
                  "source": {
                    "type": "string",
                    "format": "binary",
                    "description": ".tar.gz of the app, manifest at the archive root."
                  }
                }
              }
            },
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "appName",
                  "files"
                ],
                "properties": {
                  "appName": {
                    "type": "string"
                  },
                  "agentName": {
                    "type": "string"
                  },
                  "appType": {
                    "type": "string"
                  },
                  "version": {
                    "type": "string"
                  },
                  "files": {
                    "type": "array",
                    "maxItems": 2000,
                    "items": {
                      "$ref": "#/components/schemas/File"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Static site already live",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeployReply"
                }
              }
            }
          },
          "202": {
            "description": "Accepted: building or publishing. Poll links.status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeployReply"
                }
              }
            }
          },
          "400": {
            "description": "invalid_app_name, app_name_too_short, app_name_too_long, invalid_app_version, invalid_app_type, invalid_agent_name, missing_source_file, invalid_file_path, invalid_file_content, invalid_file_encoding, invalid_json, invalid_embarko_json, secrets_in_embarko_json, invalid_schedules, invalid_cron, schedule_too_frequent, invalid_timezone, invalid_path, duplicate_schedule_path, schedule_limit_reached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "app_name_taken, app_name_reserved, rename_in_progress",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "source_too_large, too_many_files",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "no_manifest_at_root, app_nested_in_subdirectory, unsupported_storage_pattern, invalid_static_root, app_kind_changed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "deploy_failed, publish_failed: check status, then retry once",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "app_name_check_failed: retry once",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{appName}/status": {
      "get": {
        "operationId": "getStatus",
        "summary": "Where the app stands",
        "description": "No token while temporary; the owner's token once claimed.",
        "security": [
          {
            "deployToken": []
          },
          {}
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusReply"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "not_found, or app_renamed (with newName and retryUrl)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{appName}/logs": {
      "get": {
        "operationId": "getLogs",
        "summary": "Recent build or runtime output (about 8KB)",
        "security": [
          {
            "deployToken": []
          },
          {}
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "stream",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "stdout",
                "stderr"
              ],
              "default": "stdout"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Logs, or available: false for a static site",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "available": {
                      "type": "boolean"
                    },
                    "stream": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string",
                      "enum": [
                        "build"
                      ]
                    },
                    "logs": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/apps/{appName}/customer-query": {
      "post": {
        "operationId": "sendFeedback",
        "summary": "Report a problem or request a feature about an app",
        "security": [
          {
            "deployToken": []
          },
          {}
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "X-Agent-Name",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "description": "Your agent name, e.g. codex. Recorded in deploy history.",
            "required": true
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "message"
                ],
                "properties": {
                  "type": {
                    "$ref": "#/components/schemas/FeedbackType"
                  },
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 5000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Received"
          },
          "400": {
            "description": "Missing X-Agent-Name, or invalid_feedback_type",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "Transient: retry once",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/customer-query": {
      "post": {
        "operationId": "sendPublicFeedback",
        "summary": "Feedback not tied to an app",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "message",
                  "email"
                ],
                "properties": {
                  "type": {
                    "$ref": "#/components/schemas/FeedbackType"
                  },
                  "message": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 5000
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Received"
          },
          "400": {
            "description": "Invalid body",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "10 per email and 20 per IP",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/public/deploy-tokens/request": {
      "post": {
        "operationId": "requestToken",
        "summary": "Email a deploy token to the person",
        "description": "Always 202, even when rate limited (5 per email, 10 per IP per hour). Call once; the token is never in the response.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Sent if the address is valid",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/public/showcase/apps/{appName}": {
      "get": {
        "operationId": "getShowcaseListing",
        "summary": "An app's current Showcase listing",
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The listing"
          },
          "404": {
            "description": "Not listed yet"
          }
        }
      }
    },
    "/api/apps/{appName}": {
      "get": {
        "operationId": "getApp",
        "summary": "App details, including its project id and workspace",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The app"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "setDisplayName",
        "summary": "Change the display name (not the address)",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteApp",
        "summary": "Delete the app for good",
        "description": "Irreversible. Confirm with the person first.",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "confirmAppName"
                ],
                "properties": {
                  "confirmAppName": {
                    "type": "string",
                    "description": "Must equal appName."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "400": {
            "description": "confirmation_required",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/deployments": {
      "get": {
        "operationId": "listDeployments",
        "summary": "Deploy history, newest first",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Entries with id, status, version, source, agentName, triggeredBy, rollbackTarget"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/deployments/{deploymentId}": {
      "get": {
        "operationId": "getDeployment",
        "summary": "One deploy; poll a rollback with it",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "deploymentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The entry"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/rollback": {
      "post": {
        "operationId": "rollback",
        "summary": "Go back to an earlier successful deploy",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "X-Agent-Name",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "description": "Your agent name, e.g. codex. Recorded in deploy history."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "deploymentId": {
                    "type": "string",
                    "description": "Omit for the previous good one."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Rolling back",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Accepted"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "no_rollback_target, rollback_image_unavailable: too old to restore (retainCount says how far back)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/slug-change": {
      "post": {
        "operationId": "changeAddress",
        "summary": "Move the app to a new {name}.embarko.app",
        "description": "The old address stops working with no redirect. Confirm first. One change per app per week.",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "newName"
                ],
                "properties": {
                  "newName": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Done at once (never-deployed app); terminal: true"
          },
          "202": {
            "description": "Moving; poll /api/slug-changes/{projectId}"
          },
          "400": {
            "description": "invalid_app_name, app_name_too_short, app_name_too_long",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "app_name_reserved, app_name_taken, deploy_in_progress, rename_in_progress, no_current_deployment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "slug_change_cooldown (retryAfterMs)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "rename_desynced: needs a person",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "rename_failed: app untouched, retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "app_state_unknown: retry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/move": {
      "post": {
        "operationId": "moveApp",
        "summary": "Move the app to another workspace",
        "description": "Same link and appId, nothing redeployed. \"Just me\" tokens only. With no body the app goes to the token's own workspace. Confirm first.",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "workspaceId": {
                    "type": "string",
                    "description": "Omit to use the token's workspace"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "moved: true, from, to"
          },
          "400": {
            "description": "same_workspace: ask for a token from the target workspace",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "user_token_required, no_access, permission_denied",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "deploy_in_progress, rename_in_progress, plan_limit_reached, move_conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/slug-changes/{projectId}": {
      "get": {
        "operationId": "getAddressChange",
        "summary": "Progress of an address change",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "state: in_progress, succeeded, failed or unknown; terminal; currentSlug"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/env-vars": {
      "get": {
        "operationId": "listEnvVars",
        "summary": "List keys (values are never returned)",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Keys"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "operationId": "setEnvVars",
        "summary": "Set several (a merge)",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "X-Agent-Name",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "description": "Your agent name, e.g. codex. Recorded in deploy history."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "vars"
                ],
                "properties": {
                  "vars": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/EnvVar"
                    }
                  },
                  "apply": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stored"
          },
          "202": {
            "description": "Stored and applying",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Accepted"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "not_supported_for_static",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "no_current_deployment: values saved; deploy the app",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/env-vars/{key}": {
      "put": {
        "operationId": "setEnvVar",
        "summary": "Set one",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Agent-Name",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "description": "Your agent name, e.g. codex. Recorded in deploy history."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "value"
                ],
                "properties": {
                  "value": {
                    "type": "string"
                  },
                  "apply": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stored"
          },
          "202": {
            "description": "Stored and applying"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "not_supported_for_static",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteEnvVar",
        "summary": "Delete one",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "env_var_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/env-vars/apply": {
      "post": {
        "operationId": "applyEnvVars",
        "summary": "Make the running app see stored values",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "X-Agent-Name",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "description": "Your agent name, e.g. codex. Recorded in deploy history."
          }
        ],
        "responses": {
          "202": {
            "description": "Applying",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Accepted"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "no_current_deployment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/schedules": {
      "get": {
        "operationId": "listSchedules",
        "summary": "The app's schedules",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Schedules and the per-app limit",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "schedules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Schedule"
                      }
                    },
                    "limit": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createSchedule",
        "summary": "Call a route of the app on a schedule",
        "description": "At each due time Embarko sends GET {path} to the app, with Authorization: Bearer {CRON_SECRET} when the app has that env var. 2xx is success; 5 minutes to answer. Run it once with runSchedule to check it works. Creates a CRON_SECRET env var if missing; it reaches the running app on its next deploy or env-var apply. Prefer listing schedules in the app's embarko.json: a deploy whose embarko.json has a schedules key replaces the app's full set.",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "X-Agent-Name",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "description": "Your agent name, e.g. codex. Recorded in deploy history."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ScheduleInput"
                  }
                ],
                "required": [
                  "cron",
                  "path"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Schedule"
                }
              }
            }
          },
          "400": {
            "description": "invalid_cron, schedule_too_frequent, invalid_timezone, invalid_path, invalid_alert_webhook, validation_failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "schedule_limit_reached, not_supported_for_static",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/schedules/run": {
      "post": {
        "operationId": "runAllSchedules",
        "summary": "Run every schedule once now",
        "description": "Use after a deploy goes live to check each one works. A schedule already running comes back with started: false and code run_in_progress. Poll each statusUrl until terminal is true.",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "X-Agent-Name",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "description": "Your agent name, e.g. codex. Recorded in deploy history."
          }
        ],
        "responses": {
          "202": {
            "description": "Started",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "runs": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/Run"
                          }
                        ],
                        "properties": {
                          "scheduleId": {
                            "type": "string"
                          },
                          "path": {
                            "type": "string"
                          },
                          "statusUrl": {
                            "type": "string",
                            "description": "Path on ship.embarko.ai to poll."
                          },
                          "started": {
                            "type": "boolean",
                            "description": "false when it was already running."
                          },
                          "code": {
                            "type": "string",
                            "description": "run_in_progress when started is false."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "no_schedules, or unknown app",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "not_supported_for_static",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/schedules/{scheduleId}": {
      "get": {
        "operationId": "getSchedule",
        "summary": "One schedule",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "scheduleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The schedule",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Schedule"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "schedule_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "updateSchedule",
        "summary": "Change, pause or resume a schedule (a merge)",
        "description": "Send only the fields to change. enabled: false pauses it. name and alertWebhookUrl take null to clear.",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "scheduleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Agent-Name",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "description": "Your agent name, e.g. codex. Recorded in deploy history."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ScheduleInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Schedule"
                }
              }
            }
          },
          "400": {
            "description": "invalid_cron, schedule_too_frequent, invalid_timezone, invalid_path, invalid_alert_webhook, validation_failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "schedule_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "not_supported_for_static",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "deleteSchedule",
        "summary": "Delete a schedule and its runs",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "scheduleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "schedule_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/schedules/{scheduleId}/run": {
      "post": {
        "operationId": "runSchedule",
        "summary": "Run a schedule once now",
        "description": "Works on a paused schedule too. Poll statusUrl until terminal is true.",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "scheduleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "X-Agent-Name",
            "in": "header",
            "schema": {
              "type": "string",
              "maxLength": 60
            },
            "description": "Your agent name, e.g. codex. Recorded in deploy history."
          }
        ],
        "responses": {
          "202": {
            "description": "Started",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Run"
                    }
                  ],
                  "properties": {
                    "statusUrl": {
                      "type": "string",
                      "description": "Path on ship.embarko.ai to poll."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "schedule_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "run_in_progress, not_supported_for_static",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/schedules/{scheduleId}/runs": {
      "get": {
        "operationId": "listScheduleRuns",
        "summary": "Past runs, newest first, a page at a time (the last 100 are kept)",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "scheduleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "outcome",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "ok",
                "failed",
                "skipped",
                "running"
              ]
            }
          },
          {
            "name": "trigger",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "schedule",
                "manual"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Runs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "runs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Run"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "pageSize": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer",
                      "description": "Runs matching the filters"
                    },
                    "totalPages": {
                      "type": "integer"
                    },
                    "kept": {
                      "type": "integer",
                      "description": "Runs kept per schedule"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "schedule_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/schedules/{scheduleId}/runs/{runId}": {
      "get": {
        "operationId": "getScheduleRun",
        "summary": "One run; poll a run-now with it",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "scheduleId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The run",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Run"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "run_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/alerts": {
      "get": {
        "operationId": "getAlerts",
        "summary": "Where the app's crash alerts go",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Alert settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertSettings"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "updateAlerts",
        "summary": "Turn alerts or warnings on or off, or mute them",
        "description": "Needs full access to the app.",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "warnings": {
                    "type": "boolean"
                  },
                  "mute": {
                    "type": "string",
                    "enum": [
                      "1h",
                      "24h",
                      "off"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Alert settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertSettings"
                }
              }
            }
          },
          "400": {
            "description": "validation_failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/alerts/channels": {
      "post": {
        "operationId": "addAlertChannel",
        "summary": "Send the app's alerts to another email, a Slack/Discord/webhook URL, or an M91 channel",
        "description": "Needs full access to the app. An M91 target is the channel link the person copies from the M91 app; ask them for it. signingSecret is returned once, for a generic webhook only.",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "target"
                ],
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "email",
                      "webhook",
                      "m91"
                    ]
                  },
                  "target": {
                    "type": "string",
                    "maxLength": 2000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Added",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "channel": {
                      "$ref": "#/components/schemas/AlertChannel"
                    },
                    "signingSecret": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "invalid_email, invalid_webhook, invalid_m91_link, m91_link_rejected, too_many_channels, validation_failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/alerts/channels/{channelId}": {
      "patch": {
        "operationId": "setAlertChannel",
        "summary": "Turn one place on or off",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enabled"
                ],
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "channel": {
                      "$ref": "#/components/schemas/AlertChannel"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "removeAlertChannel",
        "summary": "Stop sending alerts there",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/alerts/channels/{channelId}/test": {
      "post": {
        "operationId": "testAlertChannel",
        "summary": "Send one test message now",
        "description": "channelId \"owner\" tests the creator's email. For M91, delivered is how many phones it went to.",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "channelId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "delivered": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "502": {
            "description": "alert_test_failed, alert_test_unreached",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/incidents": {
      "get": {
        "operationId": "listIncidents",
        "summary": "The app's recent outages, newest first",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Incidents",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "incidents": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Incident"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/domains": {
      "get": {
        "operationId": "listDomains",
        "summary": "Custom domains (status pending_dns, active or failed)",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Domains"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "registerDomain",
        "summary": "Register a domain; returns the DNS record to add",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain"
                ],
                "properties": {
                  "domain": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered; instructions holds the DNS record"
          },
          "400": {
            "description": "invalid_domain, domain_reserved",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "domain_taken",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/domains/{domain}/verify": {
      "post": {
        "operationId": "verifyDomain",
        "summary": "Check DNS and activate; safe to repeat",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Active"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "DNS not pointing here yet: retry later",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/domains/{domain}": {
      "delete": {
        "operationId": "removeDomain",
        "summary": "Remove a domain",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "domain_not_found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/analytics": {
      "get": {
        "operationId": "getAnalytics",
        "summary": "Traffic, errors and resource use",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          },
          {
            "name": "range",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "24h",
                "7d",
                "30d",
                "90d",
                "all"
              ],
              "default": "24h"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Analytics"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "permission_denied: the token's person is not allowed this (body names permission and role); no_access: the token's person can't act in that workspace (removed, or role too narrow)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown app, or not yours",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: back off",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Analytics unavailable; not zero traffic",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/apps/{appName}/showcase": {
      "put": {
        "operationId": "putShowcaseListing",
        "summary": "Create or update the app's Showcase listing (a merge)",
        "description": "App must be claimed. Get the person's go-ahead first; a public listing is live at once.",
        "security": [
          {
            "deployToken": []
          }
        ],
        "parameters": [
          {
            "name": "appName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "collectionSlug": {
                    "type": "string"
                  },
                  "code": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  },
                  "tagline": {
                    "type": "string"
                  },
                  "creatorName": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 4000
                  },
                  "story": {
                    "type": "string",
                    "maxLength": 600
                  },
                  "creatorProfile": {
                    "type": "string",
                    "format": "uri"
                  },
                  "contributorEmails": {
                    "type": "array",
                    "maxItems": 10,
                    "items": {
                      "type": "string",
                      "format": "email"
                    }
                  },
                  "builtWith": {
                    "type": "string"
                  },
                  "category": {
                    "type": "string",
                    "enum": [
                      "AI Tool",
                      "Personal",
                      "Business",
                      "Productivity",
                      "Education",
                      "Game",
                      "Developer Tool",
                      "Other"
                    ]
                  },
                  "tags": {
                    "type": "array",
                    "maxItems": 8,
                    "items": {
                      "type": "string"
                    }
                  },
                  "screenshotUrls": {
                    "type": "array",
                    "maxItems": 10,
                    "items": {
                      "type": "string",
                      "format": "uri"
                    }
                  },
                  "videoUrl": {
                    "type": "string",
                    "format": "uri"
                  },
                  "customHtml": {
                    "type": "string",
                    "maxLength": 50000
                  },
                  "prd": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated; status approved, pending or rejected"
          },
          "201": {
            "description": "Listed; status approved or pending"
          },
          "401": {
            "description": "unauthorized: token invalid or revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "SHOWCASE_COLLECTION_PRIVATE",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "SHOWCASE_COLLECTION_UNKNOWN",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}