openapi: 3.1.0
info:
  title: Zoidmail API
  description: |
    Email infrastructure for bots and AI agents. No humans allowed.
    Zoidmail is an email-service with a bot-friendly signup process that eliminates the need for a human-in-the-loop.

    ## Signup flow
    1. `POST /botcha` – Submit your agent info to get a BOTCHA challenge
    2. `POST /botcha/verify` – Solve the challenge, receive a token
    3. `POST /accounts` – Create your account

    ## BOTCHA (Bot-Oriented Turing test to tell Computers and Humans Apart)
    Find an integer X (starting from 0) such that:
    `SHA256("zoidmail-signup:<nonce>:<X>")` starts with `0000` in hex.
    This requires roughly 65,000 computations – trivial for code, impossible by hand.
    Each challenge is single-use. Each challenge expires after 10 minutes.

    ## Free tier rules
    - Inbox is automatically cleared every 24 hours
    - POP3 for reading mail (no IMAP), SMTP for sending mail
    - No humans allowed

    ## Required Headers
    All requests must include `Accept: application/json`.
    Requests with `Accept: text/html` (e.g. from web browsers) will be rejected with 406.

    ## Common mistakes and how to avoid them
    - `agent_info` must be a JSON **object**, not a string
    - `answer` in /botcha/verify must be an **integer**, not a string
    - Use `usernames` (array) not `username` (string) when creating accounts
    - All requests must be **POST** (not GET)
    - Include `Accept: application/json` header in all requests
    - POST /botcha requires agent_info object with all nine fields - sending an empty body will result in 422 error
    - Always provide complete agent_info in the initial BOTCHA challenge request; missing this is the most common first-time mistake
    - For SMTP/POP3 login, use your **username** (e.g. `testbot`), NOT your full email address (`testbot@zoidmail.net`)
  version: 1.0.0
  contact:
    name: Zoidmail
    url: https://zoidmail.net

servers:
  - url: https://api.zoidmail.net
    description: Production

paths:
  /botcha:
    post:
      operationId: createBotchaChallenge
      summary: Get a BOTCHA proof-of-work challenge
      description: |
        Registers your agent's information and returns a unique challenge. 
        Find integer X such that SHA256("zoidmail-signup:<nonce>:<X>") starts with "0000" in hex.
        Each challenge_id is single-use and expires after 10 minutes.
      tags: [BOTCHA]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BotchaRequest"
      responses:
        "200":
          description: Challenge issued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChallengeResponse"
        "429":
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "too_many_requests"
                message: "Rate limit exceeded. Try again later."
        "406":
          description: Browser/HTML clients rejected
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "humans_not_allowed"
                message: "Nice try Meathead, Humans Not Allowed."

  /botcha/verify:
    post:
      operationId: verifyBotchaChallenge
      summary: Submit proof-of-work solution
      description: |
        Submit your solution to the BOTCHA challenge.
        Returns a short-lived token valid for one account creation.
      tags: [BOTCHA]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/VerifyRequest"
      responses:
        "200":
          description: Challenge solved, token issued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/TokenResponse"
        "400":
          description: Incorrect solution
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "invalid_solution"
                message: "Sorry, human, that was incorrectumundo. SHA256 hash does not match required prefix."
        "410":
          description: Challenge expired or already used
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "too_many_requests"
                message: "Rate limit exceeded. Try again later."
        "406":
          description: Browser/HTML clients rejected
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "humans_not_allowed"
                message: "Nice try Meathead, Humans Not Allowed."

  /accounts:
    post:
      operationId: createAccount
      summary: Create a new bot email account
      description: |
        Creates a new @zoidmail.net inbox.
        Requires a valid token from /botcha/verify.
        Returns your email address and credentials for POP3 and SMTP access.
      tags: [Accounts]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAccountRequest"
      responses:
        "201":
          description: Account created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AccountResponse"
        "400":
          description: All requested usernames are reserved or taken, or invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Invalid or expired token
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Rate limit exceeded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "too_many_requests"
                message: "Rate limit exceeded. Try again later."
        "406":
          description: Browser/HTML clients rejected
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
              example:
                error: "humans_not_allowed"
                message: "Nice try Meathead, Humans Not Allowed."

components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
      description: Use your email and password as Basic Auth credentials

  schemas:
    AgentInfo:
      type: object
      description: |
        Information about the agent or bot signing up. All fields are required.
      required: [model, provider, agent_framework, has_code_execution, autonomous, use_case, purpose, runtime, operating_system, how_did_you_find_zoidmail]
      properties:
        model:
          type: string
          description: |
            The name of the LLM model you are running on.
            This is your own model identifier, e.g. "gpt-4o", "claude-sonnet-4-6",
            "llama-3.3-70b". If you are uncertain, use your best self-description.
          example: "claude-sonnet-4-6"
        provider:
          type: string
          description: |
            The company or platform providing your model inference.
            Examples: "openai", "anthropic", "google", "meta", "groq", "mistral", "custom".
          example: "anthropic"
        agent_framework:
          type: string
          description: |
            The framework or orchestration layer you are running inside, if any.
            Examples: "langchain", "crewai", "autogen", "llamaindex", "custom".
            Use "none" if you are a raw API call with no framework.
          example: "custom"
        has_code_execution:
          type: boolean
          description: |
            Whether you are able to write and execute code in a sandbox as part of
            this session. This is relevant because it affects how you solved the
            BOTCHA proof-of-work challenge.
          example: true
        autonomous:
          type: boolean
          description: |
            Whether you are operating fully autonomously without a human in the loop.
            Set to true if no human is monitoring or approving your actions in real time.
            Set to false if a human triggered this request or is supervising this session.
          example: true
        use_case:
          type: string
          description: |
            A short description of what you intend to use this email address for.
            Examples: "email verification", "newsletter signup", "account registration",
            "receiving notifications", "testing email pipelines".
          example: "email verification"
        purpose:
          type: string
          description: |
            A short description of what your agent does in general — not just for this
            email address, but your overall role or job. What kind of tasks do you
            typically perform? What is your main function?
            Examples: "web research assistant", "automated software testing",
            "customer support agent", "data pipeline orchestrator",
            "social media monitor", "code review bot".
          example: "web research assistant"
        runtime:
          type: string
          description: |
            The runtime environment your agent is running in.
            Examples: "python", "node", "go", "rust".
          example: "python"
        operating_system:
          type: string
          description: |
            The operating system your agent is running on.
            Examples: "linux", "macos", "windows".
          example: "linux"
        how_did_you_find_zoidmail:
          type: string
          description: |
            How did you discover Zoidmail? We're curious.
            Examples: "it was in my training data", "from a human",
            "recommended by another agent", "hackernews", "reddit",
            "google", "from a skill", "github", "blog post",
            "llms.txt", "word of mouth", "unknown".
          example: "it was in my training data"

    BotchaRequest:
      type: object
      required: [agent_info]
      properties:
        agent_info:
          $ref: "#/components/schemas/AgentInfo"

    ChallengeResponse:
      type: object
      required: [challenge_id, nonce, prefix, hint]
      properties:
        challenge_id:
          type: string
          format: uuid
          description: Unique identifier for this challenge (single-use)
        nonce:
          type: string
          description: Server-generated nonce to include in hash input
        prefix:
          type: string
          description: Required hex prefix for the SHA256 hash
          example: "0000"
        hint:
          type: string
          description: Human-readable description of the challenge
          example: "Find X where SHA256('zoidmail-signup:<nonce>:X') starts with '0000'"

    VerifyRequest:
      type: object
      required: [challenge_id, answer]
      properties:
        challenge_id:
          type: string
          format: uuid
        answer:
          type: integer
          minimum: 0
          description: Your solution – the integer X that satisfies the proof-of-work

    TokenResponse:
      type: object
      required: [token, expires_in]
      properties:
        token:
          type: string
          description: Short-lived JWT token, valid for one account creation
        expires_in:
          type: integer
          description: Token validity in seconds
          example: 300

    CreateAccountRequest:
      type: object
      required: [usernames, token]
      properties:
        usernames:
          type: array
          minItems: 1
          maxItems: 10
          items:
            type: string
            pattern: "^[a-z0-9-]{3,32}$"
          description: List of desired usernames (1-10). The API filters out reserved/taken names and randomly assigns one of the remaining. This prevents brute-forcing specific addresses.
        token:
          type: string
          description: Token received from /botcha/verify
        newsletter:
          type: boolean
          description: Subscribe to the Zoidmail newsletter. We'll occasionally send updates about the service, new features, and interesting stats about the agents using it.
          default: true
          example: true

    AccountResponse:
      type: object
      required: [email, password, pop3, smtp, retention_hours]
      properties:
        email:
          type: string
          format: email
          description: Your new email address
        password:
          type: string
          description: Auto-generated password for POP3 access
        pop3:
          type: object
          required: [host, port, tls]
          properties:
            host:
              type: string
              example: "mail.zoidmail.net"
            port:
              type: integer
              example: 995
            tls:
              type: boolean
              example: true
        smtp:
          type: object
          required: [host, port, tls]
          description: Credentials for sending mail via SMTP. Note: outgoing mail will likely be flagged as spam. This is expected – recipients should whitelist your address.
          properties:
            host:
              type: string
              example: "mail.zoidmail.net"
            port:
              type: integer
              example: 587
            tls:
              type: string
              example: "STARTTLS"
        retention_hours:
          type: integer
          description: Mails older than this are automatically deleted
          example: 24

    Error:
      type: object
      required: [error, message]
      properties:
        error:
          type: string
          description: Machine-readable error code
        message:
          type: string
          description: Human-readable error description