{
  "openapi": "3.1.0",
  "info": {
    "title": "Mojobase waitlist",
    "version": "1.0.0",
    "summary": "Join the Mojobase waitlist.",
    "description": "Only add an address its owner has asked you to add. Guide: https://mojobase.ai/docs/waitlist.md",
    "contact": { "email": "hello@mojobase.ai" }
  },
  "servers": [{ "url": "https://mojobase.ai" }],
  "security": [],
  "externalDocs": { "description": "Guide for agents", "url": "https://mojobase.ai/docs/waitlist.md" },
  "paths": {
    "/api/waitlist": {
      "post": {
        "operationId": "joinWaitlist",
        "summary": "Add someone to the waitlist",
        "description": "Adds an address, and optionally its owner's answers, in one request. The response is the same for new and repeat signups, so it's safe to repeat. Pass the message in the response on to your user.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Signup" } } }
        },
        "responses": {
          "200": {
            "description": "We've emailed the address a link. It's on the list once its owner clicks it.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Joined" } } }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "405": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "415": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/waitlist/confirm": {
      "post": {
        "operationId": "confirmAddress",
        "summary": "Confirm an address with the token from the emailed link",
        "description": "Only when the address's owner has asked you to and you can read that email. Opening the link in a browser shows a button that does the same.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "required": ["token"], "properties": { "token": { "type": "string" } } } } }
        },
        "responses": {
          "200": {
            "description": "The address is on the list.",
            "content": { "application/json": { "schema": { "type": "object", "required": ["ok", "status", "message"], "properties": { "ok": { "const": true }, "status": { "const": "confirmed" }, "message": { "type": "string" } } } } }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "415": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/waitlist/answers": {
      "post": {
        "operationId": "saveAnswers",
        "summary": "Save someone's answers after they've joined",
        "description": "Used by the form on the website. Agents can send the same answers to /api/waitlist instead. The response is the same whether or not the address is on the list.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Answers" } } }
        },
        "responses": {
          "200": {
            "description": "Accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["ok"],
                  "properties": { "ok": { "const": true } }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "405": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "415": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/RateLimited" },
          "500": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Wants": {
        "type": "array",
        "description": "The parts of Mojobase they want most. Unknown values are dropped.",
        "items": {
          "type": "string",
          "enum": ["email", "documents", "calendar", "customers", "tasks", "chat", "finances", "knowledge"]
        }
      },
      "Mail": {
        "type": "string",
        "description": "The email they use for work. An unknown value is dropped.",
        "enum": ["google", "microsoft", "other"]
      },
      "Signup": {
        "type": "object",
        "required": ["email"],
        "properties": {
          "email": { "type": "string", "format": "email", "maxLength": 254, "description": "The address to add." },
          "agent": { "type": "string", "maxLength": 80, "description": "Your name, such as the product you are." },
          "wants": { "$ref": "#/components/schemas/Wants" },
          "mail": { "$ref": "#/components/schemas/Mail" }
        }
      },
      "Answers": {
        "type": "object",
        "required": ["email"],
        "description": "Needs at least one known value in wants or mail.",
        "properties": {
          "email": { "type": "string", "format": "email", "maxLength": 254 },
          "wants": { "$ref": "#/components/schemas/Wants" },
          "mail": { "$ref": "#/components/schemas/Mail" }
        }
      },
      "Joined": {
        "type": "object",
        "required": ["ok", "status", "message"],
        "properties": {
          "ok": { "const": true },
          "status": { "const": "confirm_by_email" },
          "message": { "type": "string", "description": "What happens next. Pass this on to your user." },
          "answers": {
            "type": "object",
            "description": "The answers that were kept. Only present when some were sent.",
            "required": ["wants", "mail"],
            "properties": {
              "wants": { "$ref": "#/components/schemas/Wants" },
              "mail": { "oneOf": [{ "$ref": "#/components/schemas/Mail" }, { "type": "null" }] }
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "required": ["ok", "error", "message", "docs"],
        "properties": {
          "ok": { "const": false },
          "error": {
            "type": "string",
            "enum": ["bad_request", "expected_json", "invalid_email", "invalid_token", "method_not_allowed", "not_found", "server_error", "too_large"]
          },
          "message": { "type": "string", "description": "What went wrong and how to fix it." },
          "docs": { "type": "string", "description": "The path of the guide." }
        }
      }
    },
    "responses": {
      "RateLimited": {
        "description": "Too many API requests from this IP. Wait before retrying.",
        "headers": { "Retry-After": { "schema": { "type": "integer", "const": 60 }, "description": "Seconds to wait before retrying." } },
        "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": false }, "error": { "const": "rate_limited" }, "message": { "type": "string" }, "docs": { "type": "string" } }, "required": ["ok", "error", "message", "docs"] } } }
      },
      "Error": {
        "description": "The request failed, and the message says why.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  }
}
