openapi: 3.1.0
info:
  title: AZAP API
  version: "0.2.0"
  summary: Inbox first, then the agent.
  description: |
    Origin: https://azap.cloud

    Invite only. Stored inbound letters are not sent to Grok, Claude, or Kimi.
    MCP creates drafts. A person sends unless they allow a mail type.

    ## Start here

    1. Request an invite. `POST /v1/waitlist` with a recovery email. If a seat is open we email `azap_inv_…`. The JSON does not include the token.
    2. Create the inbox. `POST /v1/join` with `invite_token`, `recovery`, optional `local`. Copy `api_key` once.
    3. Read. `GET /v1/inbox` and `GET /v1/messages?folder=inbox` with `Authorization: Bearer azap_…`.
    4. Draft. `POST /v1/messages` with `mode: draft`, or `POST /mcp` `leave_briefing`.

    Same walkthrough: https://azap.cloud/developers
    OpenAPI file: https://azap.cloud/openapi.yaml
    Scalar Client: https://client.scalar.com/?url=https://azap.cloud/openapi.yaml

    Call from curl, your agent host, or the API page. Other websites cannot use the key in a browser. There is no CORS on `/v1` or `/mcp`. This spec file is the exception, so Scalar Client can load it.

    Public IMAP 993 is off. `/ops` is not in this document.
  contact:
    email: postmaster@azap.cloud
  license:
    name: Proprietary

servers:
  - url: https://azap.cloud
    description: Production

tags:
  - name: Start
    description: Waitlist, invite token, then create the inbox.
  - name: Inbox
    description: Who this box is. Agent key or human session.
  - name: Mail
    description: Letters. Needs the Read grant. Default is on.
  - name: Drafts
    description: Outbox. Agent keys stay draft only unless the owner allows send.
  - name: Threads
    description: Conversation groups in this inbox.
  - name: MCP
    description: JSON-RPC tools for the agent. Draft first.
  - name: Auth
    description: Magic link, password, session cookie for the human.
  - name: Settings
    description: Human session. Webhook, grants, keys, memory, close.
  - name: Domains
    description: Send identity. Reply-To stays @azap.cloud.
  - name: Write
    description: Ask search and Write assist. Human session. Stored letters stay here.

security: []

paths:
  /v1/waitlist:
    post:
      tags: [Start]
      operationId: requestInvite
      summary: Request an invite token
      description: |
        Records the address. If a seat is open we email `azap_inv_…`.
        The token is not in this response. Recovery cannot be @azap.cloud later at join,
        but waitlist accepts a normal external inbox.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WaitlistRequest"
            example:
              email: you@gmail.com
      responses:
        "201":
          description: Recorded. emailed true means the invite left.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WaitlistResponse"
        "400":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/Error"
        "503":
          $ref: "#/components/responses/Error"

  /v1/join:
    post:
      tags: [Start]
      operationId: joinInbox
      summary: Create an inbox with an invite token
      description: |
        Body `invite_token` must start with `azap_inv_`. Recovery cannot be @azap.cloud.
        `local` is optional. Copy `api_key` once. Also sets the human session cookie for the browser UI.
        Same as `POST /v1/inboxes` with `Authorization: Bearer azap_inv_…`.
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/JoinRequest"
            example:
              invite_token: azap_inv_…
              local: ada
              recovery: you@gmail.com
      responses:
        "201":
          description: Inbox created. api_key is shown once.
          headers:
            Set-Cookie:
              schema:
                type: string
              description: Human session cookie __Host-azap
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JoinResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"
        "429":
          $ref: "#/components/responses/Error"

  /v1/inboxes:
    post:
      tags: [Start]
      operationId: provisionInbox
      summary: Create an inbox (Bearer invite)
      description: Same create as `/v1/join`, with the invite in Authorization.
      security:
        - InviteToken: []
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProvisionRequest"
      responses:
        "201":
          description: Inbox created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JoinResponse"
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"

  /v1/inbox:
    get:
      tags: [Inbox]
      operationId: getInbox
      summary: Read this inbox
      security:
        - AgentKey: []
        - HumanSession: []
      responses:
        "200":
          description: Inbox metadata. No message bodies.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Inbox"
        "401":
          $ref: "#/components/responses/Error"

  /v1/inbox/live:
    get:
      tags: [Inbox]
      operationId: inboxLive
      summary: Live inbox WebSocket
      description: Human session only. Upgrade to WebSocket. New mail events, not full bodies.
      security:
        - HumanSession: []
      responses:
        "101":
          description: Switched to WebSocket.
        "401":
          $ref: "#/components/responses/Error"
        "426":
          $ref: "#/components/responses/Error"

  /v1/messages:
    get:
      tags: [Mail]
      operationId: listMessages
      summary: List letters
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - name: folder
          in: query
          schema:
            $ref: "#/components/schemas/Folder"
          description: inbox, sent, spam, archive, or trash.
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/SearchQ"
        - $ref: "#/components/parameters/Before"
        - name: thread
          in: query
          schema:
            type: string
        - name: actor
          in: query
          schema:
            type: string
            enum: [human, agent]
      responses:
        "200":
          description: Page of letters.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/MessageList"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"
    post:
      tags: [Mail]
      operationId: createMessage
      summary: Create a draft (or send if allowed)
      description: |
        Agent keys are draft only. Pass `mode: draft`. Empty body drafts are allowed. Send needs a body.
        Do not send `from`. Idempotency-Key is honoured.
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateMessageRequest"
            example:
              to: you@example.com
              subject: Hello
              text: Hi from AZAP.
              mode: draft
      responses:
        "202":
          description: Draft in Outbox. Not sent.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DraftCreated"
        "200":
          description: Sent, only if the owner allowed send for this mail type.
        "400":
          $ref: "#/components/responses/Error"
        "401":
          $ref: "#/components/responses/Error"
        "403":
          $ref: "#/components/responses/Error"

  /v1/messages/{id}:
    get:
      tags: [Mail]
      operationId: getMessage
      summary: Open one letter
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          description: Letter plus attachments metadata.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Message"
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"

  /v1/messages/{id}/raw:
    get:
      tags: [Mail]
      operationId: getMessageRaw
      summary: RFC822 of one letter
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          description: Raw message.
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"

  /v1/messages/{id}/attachments/{attId}:
    get:
      tags: [Mail]
      operationId: getMessageAttachment
      summary: Download a letter attachment
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
        - $ref: "#/components/parameters/AttId"
      responses:
        "200":
          description: File bytes.
        "401":
          $ref: "#/components/responses/Error"
        "404":
          $ref: "#/components/responses/Error"

  /v1/messages/{id}/folder:
    post:
      tags: [Mail]
      operationId: setMessageFolder
      summary: Move a letter
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [folder]
              properties:
                folder:
                  $ref: "#/components/schemas/Folder"
      responses:
        "200":
          $ref: "#/components/responses/Ok"
        "400":
          $ref: "#/components/responses/Error"

  /v1/messages/{id}/read:
    post:
      tags: [Mail]
      operationId: setMessageRead
      summary: Mark read or unread
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                read:
                  type: boolean
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/messages/{id}/reply:
    post:
      tags: [Mail]
      operationId: replyMessage
      summary: Draft a reply
      description: Creates a draft in Outbox. Agent keys do not send.
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ReplyRequest"
      responses:
        "202":
          description: Draft created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DraftCreated"
        "400":
          $ref: "#/components/responses/Error"

  /v1/messages/{id}/briefing:
    post:
      tags: [Mail]
      operationId: leaveBriefingRest
      summary: Leave a briefing on a letter
      description: Same job as MCP leave_briefing. The person sees Reply this way.
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BriefingRequest"
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/messages/{id}/prepare:
    post:
      tags: [Mail]
      operationId: prepareBriefing
      summary: Local extractive note
      description: Builds a note from the letter and this box memory. Does not call a model vendor.
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/messages/{id}/similar:
    post:
      tags: [Mail]
      operationId: teachSimilar
      summary: Remember this letter for similar replies
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reply]
              properties:
                reply:
                  type: string
                why:
                  type: string
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/messages/{id}/triage:
    post:
      tags: [Mail]
      operationId: triageMessage
      summary: Agent triage of one letter
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/messages/{id}/suggest:
    post:
      tags: [Mail]
      operationId: suggestReply
      summary: Suggested reply (human)
      description: Human session. Stored inbound letters are not sent to a model vendor.
      security:
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                instruction:
                  type: string
      responses:
        "200":
          $ref: "#/components/responses/Ok"
        "403":
          $ref: "#/components/responses/Error"

  /v1/drafts:
    get:
      tags: [Drafts]
      operationId: listDrafts
      summary: List Outbox
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/SearchQ"
        - $ref: "#/components/parameters/Before"
        - name: actor
          in: query
          schema:
            type: string
            enum: [human, agent]
      responses:
        "200":
          description: Pending drafts.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DraftList"

  /v1/drafts/{id}:
    get:
      tags: [Drafts]
      operationId: getDraft
      summary: Open one draft
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Draft"
        "404":
          $ref: "#/components/responses/Error"
    post:
      tags: [Drafts]
      operationId: updateDraft
      summary: Edit a pending draft
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateDraftRequest"
      responses:
        "200":
          content:
            application/json:
              schema:
                type: object
                properties:
                  draft:
                    $ref: "#/components/schemas/Draft"

  /v1/drafts/{id}/approve:
    post:
      tags: [Drafts]
      operationId: approveDraft
      summary: Send a draft
      description: Human session. The person sends.
      security:
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          description: Sent.
        "401":
          $ref: "#/components/responses/Error"
        "409":
          $ref: "#/components/responses/Error"

  /v1/drafts/{id}/reject:
    post:
      tags: [Drafts]
      operationId: rejectDraft
      summary: Drop a draft
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/drafts/{id}/attachments/{attId}:
    get:
      tags: [Drafts]
      operationId: getDraftAttachment
      summary: Download a draft attachment
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
        - $ref: "#/components/parameters/AttId"
      responses:
        "200":
          description: File bytes.

  /v1/threads:
    get:
      tags: [Threads]
      operationId: listThreads
      summary: List threads
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/threads/{id}:
    get:
      tags: [Threads]
      operationId: getThread
      summary: Open one thread
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          $ref: "#/components/responses/Ok"
        "404":
          $ref: "#/components/responses/Error"

  /v1/threads/{id}/summary:
    post:
      tags: [Threads]
      operationId: leaveThreadSummary
      summary: Leave a summary you wrote
      description: AZAP does not send the thread to a model vendor.
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [summary]
              properties:
                summary:
                  type: string
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /mcp:
    get:
      tags: [MCP]
      operationId: mcpDiscover
      summary: List MCP tools (no key)
      description: Public discovery. Tool names only. No inbox data.
      responses:
        "200":
          description: Tool names and draft_only note.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/McpDiscover"
    post:
      tags: [MCP]
      operationId: mcpRpc
      summary: MCP JSON-RPC
      description: |
        Methods: initialize, tools/list, tools/call, ping.

        Typical loop: describe_inbox, read_memory, list_needs_attention, read the letter on your side,
        research on your side, leave_briefing.

        send_email and reply_to_thread create drafts. draft_ai_reply and label_message refuse stored mail
        to vendors. Do not put keys in tool arguments.
      security:
        - AgentKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/JsonRpcRequest"
            examples:
              describe:
                summary: describe_inbox
                value:
                  jsonrpc: "2.0"
                  id: 1
                  method: tools/call
                  params:
                    name: describe_inbox
                    arguments: {}
              briefing:
                summary: leave_briefing
                value:
                  jsonrpc: "2.0"
                  id: 2
                  method: tools/call
                  params:
                    name: leave_briefing
                    arguments:
                      message_id: MESSAGE_ID
                      summary: They asked for a date.
                      why: They want a yes or no.
                      reply: Yes, Thursday works.
      responses:
        "200":
          description: JSON-RPC result or error.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/JsonRpcResponse"
        "401":
          $ref: "#/components/responses/Error"

  /v1/auth/magic-link:
    post:
      tags: [Auth]
      operationId: magicLink
      summary: Email a login link
      description: Always looks the same from outside. Recovery is an external inbox, never @azap.cloud.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [address]
              properties:
                address:
                  type: string
                  example: ada@azap.cloud
      responses:
        "200":
          description: Generic ok.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  message:
                    type: string

  /v1/auth/consume:
    post:
      tags: [Auth]
      operationId: consumeMagicLink
      summary: Open the login link
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [token]
              properties:
                token:
                  type: string
      responses:
        "200":
          description: Session cookie set.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthOk"
        "401":
          $ref: "#/components/responses/Error"

  /v1/auth/password:
    post:
      tags: [Auth]
      operationId: passwordLogin
      summary: Sign in with password
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [address, password]
              properties:
                address:
                  type: string
                password:
                  type: string
                  format: password
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthOk"
        "401":
          $ref: "#/components/responses/Error"

  /v1/auth/logout:
    post:
      tags: [Auth]
      operationId: logout
      summary: End this session
      security:
        - HumanSession: []
        - AgentKey: []
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/auth/session:
    post:
      tags: [Auth]
      operationId: adoptSession
      summary: Set the session cookie from a human token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [token]
              properties:
                token:
                  type: string
                  description: azap_hs_…
      responses:
        "200":
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AuthOk"

  /v1/inbox/password:
    post:
      tags: [Settings]
      operationId: setPassword
      summary: Set or clear the inbox password
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                password:
                  type: string
                current:
                  type: string
                clear:
                  type: boolean
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/recovery:
    post:
      tags: [Settings]
      operationId: setRecovery
      summary: Set recovery email
      description: Cannot be @azap.cloud.
      security:
        - HumanSession: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [email]
              properties:
                email:
                  type: string
                  format: email
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/webhook:
    post:
      tags: [Settings]
      operationId: setWebhook
      summary: Set or clear the webhook
      description: |
        Human session. HTTPS only. Empty url clears it.
        We POST event name and id, not mail bodies. Events include message.received,
        draft.pending, draft.resolved. Secret is shown once as azap_wh_…
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
      responses:
        "200":
          description: webhook true includes secret once.
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
                  webhook:
                    type: boolean
                  secret:
                    type: string

  /v1/inbox/keys/rotate:
    post:
      tags: [Settings]
      operationId: rotateAgentKey
      summary: Rotate the agent key
      description: Human session. New azap_ key shown once.
      security:
        - HumanSession: []
      responses:
        "200":
          content:
            application/json:
              schema:
                type: object
                properties:
                  api_key:
                    type: string
                  shown_once:
                    type: boolean
                  draft_only:
                    type: boolean

  /v1/inbox/agent-grant:
    post:
      tags: [Settings]
      operationId: setAgentGrant
      summary: Read and draft grants for the agent
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                read:
                  type: boolean
                  default: true
                draft:
                  type: boolean
                  default: true
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/allow-send:
    post:
      tags: [Settings]
      operationId: setAllowSend
      summary: Allow or stop agent send
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                enabled:
                  type: boolean
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/auto-send:
    post:
      tags: [Settings]
      operationId: setAutoSend
      summary: Allow listed mail types to leave without a click
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                labels:
                  type: array
                  items:
                    type: string
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/ai:
    post:
      tags: [Write]
      operationId: setWriteAssist
      summary: Turn Write assist on or off
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EnabledBody"
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/ask:
    post:
      tags: [Write]
      operationId: setAskInbox
      summary: Turn Ask search on or off
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EnabledBody"
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/similar-auto:
    post:
      tags: [Settings]
      operationId: setSimilarAuto
      summary: Auto draft for similar letters
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/EnabledBody"
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/ai-key:
    post:
      tags: [Write]
      operationId: setAiKey
      summary: Store a Write assist key
      description: Human session. Prefer one OpenRouter key. Stored letters are still not sent.
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                key:
                  type: string
                provider:
                  type: string
                flavor:
                  type: string
                  enum: [mail, code]
                thinking:
                  type: string
                  enum: [off, on]
                search:
                  type: string
                  enum: [off, on]
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/ai-route:
    post:
      tags: [Write]
      operationId: setAiRoute
      summary: Pick the Write model
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                provider:
                  type: string
                  enum: [grok, claude, kimi]
                flavor:
                  type: string
                  enum: [mail, code]
                thinking:
                  type: string
                  enum: [off, on]
                search:
                  type: string
                  enum: [off, on]
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/brain:
    get:
      tags: [Settings]
      operationId: getBrain
      summary: Read this box memory
      security:
        - AgentKey: []
        - HumanSession: []
      responses:
        "200":
          $ref: "#/components/responses/Ok"
    post:
      tags: [Settings]
      operationId: resetBrain
      summary: Clear this box memory
      security:
        - HumanSession: []
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/compose/assist:
    post:
      tags: [Write]
      operationId: composeAssist
      summary: Write assist
      description: |
        Human session. Uses the instruction you type plus To, Subject, and the draft you already applied.
        Does not send stored inbound letters to a model vendor.
      security:
        - HumanSession: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [instruction]
              properties:
                instruction:
                  type: string
                to:
                  type: string
                subject:
                  type: string
                text:
                  type: string
                html:
                  type: string
      responses:
        "200":
          $ref: "#/components/responses/Ok"
        "403":
          $ref: "#/components/responses/Error"

  /v1/search/ask:
    post:
      tags: [Write]
      operationId: searchAsk
      summary: Ask this inbox
      description: Human session. Local search. The model does not read stored letters.
      security:
        - HumanSession: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [prompt]
              properties:
                prompt:
                  type: string
                history:
                  type: array
                  items:
                    type: object
                    properties:
                      role:
                        type: string
                      text:
                        type: string
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/export:
    get:
      tags: [Settings]
      operationId: exportInbox
      summary: Export this inbox
      security:
        - HumanSession: []
      responses:
        "200":
          description: Messages, drafts, audit, some raw.
        "401":
          $ref: "#/components/responses/Error"

  /v1/inbox/close:
    post:
      tags: [Settings]
      operationId: closeInbox
      summary: Close this inbox
      security:
        - HumanSession: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [confirm]
              properties:
                confirm:
                  type: string
                  description: Must equal the inbox address.
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/resume:
    post:
      tags: [Settings]
      operationId: resumeInbox
      summary: Resume a paused inbox
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                confirm:
                  type: boolean
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/sessions:
    get:
      tags: [Settings]
      operationId: listSessions
      summary: List human sessions
      security:
        - HumanSession: []
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/sessions/revoke:
    post:
      tags: [Settings]
      operationId: revokeSessions
      summary: Revoke other sessions, or one id
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                id:
                  type: string
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/drops:
    post:
      tags: [Settings]
      operationId: createDrop
      summary: Start a large file drop
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                filename:
                  type: string
                size:
                  type: integer
                content_type:
                  type: string
      responses:
        "201":
          $ref: "#/components/responses/Ok"

  /v1/inbox/drops/{id}/chunks/{n}:
    post:
      tags: [Settings]
      operationId: uploadDropChunk
      summary: Upload one drop chunk
      security:
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Id"
        - name: n
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/inbox/send-from:
    post:
      tags: [Domains]
      operationId: setSendFrom
      summary: Choose the From domain
      description: Reply-To stays the @azap.cloud address.
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                domain:
                  type: string
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/domains:
    get:
      tags: [Domains]
      operationId: listDomains
      summary: List send domains
      security:
        - AgentKey: []
        - HumanSession: []
      responses:
        "200":
          $ref: "#/components/responses/Ok"
    post:
      tags: [Domains]
      operationId: addDomain
      summary: Reserve a custom send domain
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [domain]
              properties:
                domain:
                  type: string
      responses:
        "201":
          $ref: "#/components/responses/Ok"
    delete:
      tags: [Domains]
      operationId: deleteDomain
      summary: Remove a custom send domain
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [domain]
              properties:
                domain:
                  type: string
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/domains/verify:
    post:
      tags: [Domains]
      operationId: verifyDomain
      summary: Check DNS for a reserved domain
      security:
        - HumanSession: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [domain]
              properties:
                domain:
                  type: string
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/pgp/lookup:
    get:
      tags: [Mail]
      operationId: pgpLookup
      summary: Look up a public OpenPGP key
      security:
        - AgentKey: []
        - HumanSession: []
      parameters:
        - name: email
          in: query
          schema:
            type: string
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/events:
    get:
      tags: [Settings]
      operationId: listEvents
      summary: Access log for this inbox
      security:
        - HumanSession: []
      parameters:
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          $ref: "#/components/responses/Ok"

  /v1/unsub:
    get:
      tags: [Mail]
      operationId: unsubscribe
      summary: One click unsubscribe
      parameters:
        - name: t
          in: query
          schema:
            type: string
      responses:
        "200":
          description: HTML or JSON result.

components:
  securitySchemes:
    InviteToken:
      type: http
      scheme: bearer
      bearerFormat: azap_inv_
      description: Invite token from waitlist mail or a person. Used only to create an inbox.
    AgentKey:
      type: http
      scheme: bearer
      bearerFormat: azap_
      description: Agent key from join, shown once. Default grants Read and Draft.
    HumanSession:
      type: apiKey
      in: cookie
      name: __Host-azap
      description: Browser session azap_hs_…. Cookie mutations need a same origin request.

  parameters:
    Id:
      name: id
      in: path
      required: true
      schema:
        type: string
    AttId:
      name: attId
      in: path
      required: true
      schema:
        type: string
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 20
    SearchQ:
      name: q
      in: query
      schema:
        type: string
    Before:
      name: before
      in: query
      schema:
        type: string
      description: Cursor from next_before.
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      schema:
        type: string
        maxLength: 200
      description: Optional. Honoured on create inbox and create message.

  responses:
    Ok:
      description: JSON ok payload.
      content:
        application/json:
          schema:
            type: object
            additionalProperties: true
    Error:
      description: JSON error.
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"

  schemas:
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: string
          description: |
            Common codes: unauthorized, invite_used, recovery_missing, taken, rate_limited,
            mail_stays_here, agent_grant_denied, bad_email, bad_json, not_found, invite_cap.
        message:
          type: string
        reason:
          type: string

    WaitlistRequest:
      type: object
      required: [email]
      properties:
        email:
          type: string
          format: email
          example: you@gmail.com

    WaitlistResponse:
      type: object
      properties:
        ok:
          type: boolean
        emailed:
          type: boolean
          description: True if invite mail left.
        invited:
          type: boolean
          description: True if a token was minted. Still not returned here.
        message:
          type: string

    JoinRequest:
      type: object
      required: [invite_token, recovery]
      properties:
        invite_token:
          type: string
          description: Starts with azap_inv_
        local:
          type: string
          description: Optional local-part. Generated if omitted.
        recovery:
          type: string
          format: email
          description: External inbox. Not @azap.cloud.
        password:
          type: string
          format: password

    ProvisionRequest:
      type: object
      properties:
        local:
          type: string
        recovery:
          type: string
          format: email
        password:
          type: string
          format: password

    JoinResponse:
      type: object
      properties:
        account_id:
          type: string
        inbox_id:
          type: string
        address:
          type: string
          example: ada@azap.cloud
        local:
          type: string
        api_key:
          type: string
          description: Shown once. Starts with azap_
        draft_only:
          type: boolean
        created_at:
          type: integer

    Inbox:
      type: object
      properties:
        inbox_id:
          type: string
        address:
          type: string
        local:
          type: string
        status:
          type: string
          enum: [active, paused, closed]
        allow_send:
          type: boolean
        draft_only:
          type: boolean
        can_approve:
          type: boolean
        send_from:
          type: string
        reply_to:
          type: string
        agent_grant:
          type: object
          properties:
            read:
              type: boolean
            draft:
              type: boolean
            implicit:
              type: boolean
        webhook_set:
          type: boolean
        unread_inbox:
          type: integer
        password_set:
          type: boolean
        recovery_set:
          type: boolean
        imap:
          type: object
          description: Public IMAP 993 is off in this version. ready stays false.
          properties:
            host:
              type: string
            port:
              type: integer
            tls:
              type: boolean
            username:
              type: string
            ready:
              type: boolean
        created_at:
          type: integer

    Folder:
      type: string
      enum: [inbox, sent, spam, archive, trash]

    Attachment:
      type: object
      properties:
        id:
          type: string
        filename:
          type: string
        content_type:
          type: string
        size:
          type: integer

    Message:
      type: object
      properties:
        id:
          type: string
        thread_id:
          type: string
        direction:
          type: string
        from:
          type: string
          nullable: true
        to:
          type: string
          nullable: true
        cc:
          type: string
          nullable: true
        bcc:
          type: string
          nullable: true
        subject:
          type: string
          nullable: true
        extracted_text:
          type: string
          nullable: true
        html:
          type: string
          nullable: true
        size:
          type: integer
        created_at:
          type: integer
        flagged:
          type: string
          nullable: true
        folder:
          $ref: "#/components/schemas/Folder"
        unread:
          type: boolean
        created_by:
          type: string
          nullable: true
        attachments:
          type: array
          items:
            $ref: "#/components/schemas/Attachment"

    MessageList:
      type: object
      properties:
        messages:
          type: array
          items:
            $ref: "#/components/schemas/Message"
        next_before:
          type: string
          nullable: true
        total:
          type: integer

    CreateMessageRequest:
      type: object
      required: [to, subject]
      properties:
        to:
          type: string
        cc:
          type: string
          description: Optional Cc addresses, comma or semicolon separated.
        bcc:
          type: string
          description: Optional Bcc addresses, comma or semicolon separated. Not shown to To or Cc.
        subject:
          type: string
        text:
          type: string
        html:
          type: string
        mode:
          type: string
          enum: [draft, send]
          default: draft
        in_reply_to:
          type: string
        pgp_public_key:
          type: string

    Draft:
      type: object
      properties:
        id:
          type: string
        inbox_id:
          type: string
        to:
          type: string
        cc:
          type: string
          nullable: true
        bcc:
          type: string
          nullable: true
        subject:
          type: string
          nullable: true
        text:
          type: string
          nullable: true
        html:
          type: string
          nullable: true
        status:
          type: string
        created_at:
          type: integer
        created_by:
          type: string
          nullable: true
        attachments:
          type: array
          items:
            $ref: "#/components/schemas/Attachment"

    DraftCreated:
      type: object
      properties:
        status:
          type: string
          example: draft
        reason:
          type: string
        draft:
          $ref: "#/components/schemas/Draft"

    DraftList:
      type: object
      properties:
        drafts:
          type: array
          items:
            $ref: "#/components/schemas/Draft"
        next_before:
          type: string
          nullable: true
        total:
          type: integer

    UpdateDraftRequest:
      type: object
      properties:
        to:
          type: string
        subject:
          type: string
        text:
          type: string
        html:
          type: string

    ReplyRequest:
      type: object
      properties:
        text:
          type: string
        html:
          type: string
        mode:
          type: string
          enum: [draft, send]

    BriefingRequest:
      type: object
      properties:
        summary:
          type: string
        why:
          type: string
        reply:
          type: string
        research:
          type: string

    EnabledBody:
      type: object
      properties:
        enabled:
          type: boolean

    AuthOk:
      type: object
      properties:
        ok:
          type: boolean
        address:
          type: string
        inbox_id:
          type: string

    McpDiscover:
      type: object
      properties:
        name:
          type: string
        version:
          type: string
        protocol:
          type: string
        transport:
          type: string
        auth:
          type: string
        send:
          type: string
        tools:
          type: array
          items:
            type: string
          example:
            - describe_inbox
            - read_memory
            - read_inbox
            - search_messages
            - ask_inbox
            - list_needs_attention
            - leave_briefing
            - teach_similar
            - send_email
            - reply_to_thread

    JsonRpcRequest:
      type: object
      required: [jsonrpc, method]
      properties:
        jsonrpc:
          type: string
          enum: ["2.0"]
        id:
          description: Request id
        method:
          type: string
          enum: [initialize, tools/list, tools/call, ping, notifications/initialized]
        params:
          type: object
          properties:
            name:
              type: string
              description: Tool name for tools/call
            arguments:
              type: object
              additionalProperties: true

    JsonRpcResponse:
      type: object
      properties:
        jsonrpc:
          type: string
        id: {}
        result: {}
        error:
          type: object
          properties:
            code:
              type: integer
            message:
              type: string

x-tagGroups:
  - name: Start here
    tags: [Start, MCP]
  - name: This inbox
    tags: [Inbox, Mail, Drafts, Threads]
  - name: Human
    tags: [Auth, Settings, Domains, Write]
