openapi: 3.1.0
info:
  title: PineMail
  version: 0.2.0
  summary: Transactional email API for EU organisations
  description: |
    Authenticate with a project API key (`Authorization: Bearer pm_live_...`).
    Public JSON uses snake_case. Errors are RFC 7807 problem+json.
    Send is asynchronous: `202` means queued. Poll `GET /v1/emails/{id}`.
    Repeat the same `Idempotency-Key` when retrying a POST.
  contact:
    email: contact@pinemail.app
servers:
  - url: /
    description: This instance
tags:
  - name: Emails
  - name: Domains
paths:
  /v1/emails:
    post:
      tags: [Emails]
      operationId: sendEmail
      summary: Queue a transactional email
      parameters:
        - in: header
          name: Idempotency-Key
          schema: { type: string, maxLength: 200 }
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SendEmail"
            example:
              from: no-reply@mail.example.com
              to: [ada@example.com]
              subject: Welcome
              html: "<p>Hello</p>"
              reply_to: support@example.com
      responses:
        "202":
          description: Queued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/QueuedEmail"
        "400":
          $ref: "#/components/responses/Problem"
        "401":
          $ref: "#/components/responses/Problem"
    get:
      tags: [Emails]
      operationId: listEmails
      summary: List recent emails in the project
      responses:
        "200":
          description: Newest first
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/EmailSummary"
  /v1/emails/{id}:
    get:
      tags: [Emails]
      operationId: getEmail
      summary: Fetch one email and its timeline
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Email
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/EmailDetail"
        "404":
          $ref: "#/components/responses/Problem"
  /v1/domains:
    get:
      tags: [Domains]
      operationId: listDomains
      summary: List sending domains
      responses:
        "200":
          description: Domains
    post:
      tags: [Domains]
      operationId: createDomain
      summary: Start domain verification
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [domain]
              properties:
                domain: { type: string, example: mail.example.com }
      responses:
        "201":
          description: Created, DNS records included
  /v1/domains/{id}:
    get:
      tags: [Domains]
      operationId: getDomain
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Domain and DNS records
    delete:
      tags: [Domains]
      operationId: deleteDomain
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "204":
          description: Deleted
  /v1/domains/{id}/verify:
    post:
      tags: [Domains]
      operationId: verifyDomain
      summary: Recheck SPF, DKIM and MAIL FROM
      parameters:
        - in: path
          name: id
          required: true
          schema: { type: string }
      responses:
        "200":
          description: Current verification flags
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: pm_live
  schemas:
    SendEmail:
      type: object
      required: [from, to, subject]
      properties:
        from: { type: string }
        to:
          type: array
          items: { type: string }
        cc:
          type: array
          items: { type: string }
        bcc:
          type: array
          items: { type: string }
        reply_to: { type: string }
        subject: { type: string }
        html: { type: string }
        text: { type: string }
        headers:
          type: object
          additionalProperties: { type: string }
        tags:
          type: array
          items: { type: string }
    QueuedEmail:
      type: object
      required: [id, status, created_at]
      properties:
        id: { type: string }
        status: { type: string, example: queued }
        created_at: { type: string, format: date-time }
    EmailSummary:
      type: object
      properties:
        id: { type: string }
        status: { type: string }
        subject: { type: string }
        from: { type: string }
        created_at: { type: string, format: date-time }
    EmailDetail:
      allOf:
        - $ref: "#/components/schemas/EmailSummary"
        - type: object
          properties:
            to:
              type: array
              items: { type: string }
            html: { type: string }
            text: { type: string }
    Problem:
      type: object
      properties:
        type: { type: string }
        title: { type: string }
        status: { type: integer }
        detail: { type: string }
  responses:
    Problem:
      description: Error
      content:
        application/problem+json:
          schema:
            $ref: "#/components/schemas/Problem"
security:
  - bearerAuth: []
