openapi: 3.1.0
info:
  title: MAAC GO Email API
  version: "1.0.0"
  description: |
    Send transactional email from your application. One POST per message —
    OTPs, receipts, password resets, order notifications.

    Messages sent through this API share the free-tier meter, the suppression
    list and the delivery webhooks with your marketing campaigns, so an
    unsubscribe from a campaign also stops the API from mailing that person.

    Authenticate with a key from the Developers page. A key beginning
    `sk_test_` is recorded and returns `202` but is never handed to the mail
    provider, so integration work cannot reach a real inbox.

    Billing is shared too: the first 3,000 messages a month are free, and
    anything past that draws on the same prepaid wallet your campaigns use. A
    message the provider refuses is refunded automatically.
  contact:
    name: Crescendo Lab
    email: info@cresclab.com
    url: https://edm.cresclab.com
servers:
  - url: https://edm.cresclab.com
security:
  - apiKey: []
components:
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: "`Authorization: Bearer sk_live_…` or `sk_test_…`"
  schemas:
    SendRequest:
      type: object
      required: [to, subject]
      properties:
        to:      { type: string, format: email, example: user@example.com }
        subject: { type: string, maxLength: 300, example: "Your code: 483291" }
        html:    { type: string, description: "HTML body. Either html or text is required." }
        text:    { type: string, description: "Plain-text body. Derived from html when omitted." }
        from:    { type: string, description: "Display name only. Free accounts send from the shared verified domain.", example: "Acme Support" }
        reply_to: { type: string, format: email }
        headers:
          type: object
          additionalProperties: { type: string }
          description: Extra SMTP headers.
    SendResponse:
      type: object
      properties:
        id:        { type: string, example: "142" }
        status:    { type: string, enum: [queued, sent, failed] }
        test_mode: { type: boolean }
    Message:
      type: object
      properties:
        id:           { type: string }
        to:           { type: string, format: email }
        subject:      { type: string }
        status:       { type: string, enum: [queued, sent, delivered, bounced, complained, failed] }
        test_mode:    { type: boolean }
        error:        { type: [string, "null"] }
        created_at:   { type: integer, description: Unix epoch milliseconds }
        sent_at:      { type: [integer, "null"] }
        delivered_at: { type: [integer, "null"] }
    Error:
      type: object
      properties:
        error:  { type: string }
        detail: { type: string }
paths:
  /api/email/send:
    post:
      summary: Send one email
      operationId: sendEmail
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: '#/components/schemas/SendRequest' }
      responses:
        '202':
          description: Accepted — handed to the provider (or recorded, in test mode).
          content:
            application/json:
              schema: { $ref: '#/components/schemas/SendResponse' }
        '400': { description: Missing or invalid to/subject/body, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
        '401': { description: Missing, malformed or revoked key }
        '402': { description: 'Daily cap reached, or the free allowance is used up and the prepaid wallet cannot cover the message. The body carries needed_cents, balance_cents and short_cents.' }
        '403': { description: Recipient is suppressed, or the account is frozen }
        '502': { description: The mail provider rejected the message }
  /api/email:
    get:
      summary: List recent messages
      operationId: listMessages
      responses:
        '200':
          description: The 100 most recent messages, newest first.
          content:
            application/json:
              schema: { type: array, items: { $ref: '#/components/schemas/Message' } }
  /api/email/{id}:
    get:
      summary: Get one message
      operationId: getMessage
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses:
        '200':
          description: Delivery state for one message.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Message' }
        '404': { description: No such message on this account }
