{
 "openapi": "3.1.0",
 "info": {
  "title": "SendBuzzPro API",
  "version": "1.0.0",
  "description": "Email API for apps and AI agents with per-key budgets, approvals and a kill switch."
 },
 "servers": [
  {
   "url": "/v1"
  }
 ],
 "security": [
  {
   "bearer": []
  }
 ],
 "paths": {
  "/emails": {
   "post": {
    "summary": "Send one email",
    "description": "Body: from, to, cc, bcc, reply_to, subject, html, text, headers, tags, attachments [{filename, content (base64), content_type}], scheduled_at, request_approval, topic, unsubscribe. Header: Idempotency-Key. Returns the email with its status: sent, queued, scheduled, held or suppressed.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    },
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SendEmail"
       }
      }
     }
    },
    "parameters": [
     {
      "name": "Idempotency-Key",
      "in": "header",
      "schema": {
       "type": "string",
       "maxLength": 200
      }
     }
    ]
   },
   "get": {
    "summary": "List emails",
    "description": "Query: status (comma separated), to, key_id, broadcast_id, before, limit (≤100). Returns {data, has_more}.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/emails/batch": {
   "post": {
    "summary": "Send up to 500 emails",
    "description": "Body: an array of email objects. Returns {data, errors}; each email is accepted or refused on its own.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/emails/{id}": {
   "get": {
    "summary": "Get an email",
    "description": "Status, reason and event timeline.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/emails/{id}/raw": {
   "get": {
    "summary": "Raw message",
    "description": "The exact MIME message (message/rfc822).",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/emails/{id}/cancel": {
   "post": {
    "summary": "Cancel",
    "description": "Scheduled, queued or held emails.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/emails/{id}/approve": {
   "post": {
    "summary": "Approve a held email",
    "description": "Console only (owner, admin or approver). Body: note (optional). Who approved it and the note are kept in the timeline and the audit log.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/emails/{id}/reject": {
   "post": {
    "summary": "Reject a held email",
    "description": "Console only (owner, admin or approver). Body: note (the reason).",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/inboxes": {
   "post": {
    "summary": "Create an inbox",
    "description": "Body: username (optional; random if empty), domain (your verified domain with the receiving MX record, or empty for the shared domain), name. Use username \"*\" on your own domain for a catch-all.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "get": {
    "summary": "List inboxes",
    "description": "Addresses and unread conversation counts.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/inboxes/{id}/threads": {
   "get": {
    "summary": "List conversations",
    "description": "Query: unread=true, limit, before.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/inboxes/{id}/send": {
   "post": {
    "summary": "Start a conversation",
    "description": "Same body as /v1/emails; sent from the inbox address so replies come back to it.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/threads/{id}": {
   "get": {
    "summary": "Get a conversation",
    "description": "Every received and sent message, oldest first.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "patch": {
    "summary": "Mark read / unread",
    "description": "Body: {\"read\": true}.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/messages/{id}": {
   "get": {
    "summary": "Get a received message",
    "description": "Text, HTML, attachments list, SPF/DKIM/DMARC/spam verdicts, is_auto (auto-replies, lists, bounces).",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/messages/{id}/attachments/{index}": {
   "get": {
    "summary": "Download an attachment",
    "description": "",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/messages/{id}/reply": {
   "post": {
    "summary": "Reply in the conversation",
    "description": "Body: text, html, reply_all, request_approval, attachments. Sets In-Reply-To and References. Same budgets as /v1/emails.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/budget": {
   "get": {
    "summary": "Remaining budget",
    "description": "This key's per-minute/hour/day/month use and limits, and the account plan.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/domains": {
   "get": {
    "summary": "List domains",
    "description": "With DNS records and per-record status.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/domains/{id}/check": {
   "post": {
    "summary": "Check DNS now",
    "description": "",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/webhooks": {
   "get": {
    "summary": "List webhooks",
    "description": "",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "post": {
    "summary": "Add a webhook",
    "description": "Body: url, events (array, or [\"*\"]). Returns the signing secret once.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/webhooks/deliveries": {
   "get": {
    "summary": "Delivery log",
    "description": "Query: webhook_id, status (pending | delivered | dead).",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/webhooks/replay": {
   "post": {
    "summary": "Replay deliveries",
    "description": "Body: delivery_id, or webhook_id to replay every dead delivery.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/contacts": {
   "post": {
    "summary": "Add or update a contact",
    "description": "Body: email, first_name, last_name, data, topics {topic_id: true|false}. Only people who opted in.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "get": {
    "summary": "List contacts",
    "description": "Query: search, topic, limit, offset.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/topics": {
   "get": {
    "summary": "List topics",
    "description": "",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/broadcasts": {
   "get": {
    "summary": "List broadcasts",
    "description": "",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/suppressions": {
   "get": {
    "summary": "List suppressed addresses",
    "description": "Query: search, reason (bounce | complaint | unsubscribed | manual).",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "post": {
    "summary": "Suppress an address",
    "description": "Body: email.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/approvals": {
   "get": {
    "summary": "Recent approval decisions",
    "description": "Console only. Who approved or rejected each email, their role and note.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/members": {
   "get": {
    "summary": "List team members",
    "description": "Console only. Members, roles, pending invites and seats {used, limit}.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "post": {
    "summary": "Invite a member",
    "description": "Console only (owner or admin). Body: email, role (admin | developer | approver | viewer). Emails a one-time invite link, or returns invite_url when the server has no NOTIFY_FROM.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/members/{id}": {
   "patch": {
    "summary": "Change a role",
    "description": "Console only (owner or admin). Body: role. Applies to the member's sessions right away.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   },
   "delete": {
    "summary": "Remove a member",
    "description": "Console only (owner or admin). Signs them out everywhere.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/members/{id}/link": {
   "post": {
    "summary": "Resend an invite / sign-in link",
    "description": "Console only (owner or admin). Emailed, or returned as url when the server has no NOTIFY_FROM.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/login/email": {
   "post": {
    "summary": "Email me a sign-in link",
    "description": "No auth. Body: email. Same answer whether or not the address is a member.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/login/link": {
   "post": {
    "summary": "Use a sign-in link",
    "description": "No auth. Body: token. One use, 15 minutes. Sets the session cookie.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/invites/accept": {
   "post": {
    "summary": "Accept an invite",
    "description": "No auth. Body: token, name. One use, 7 days. Sets the session cookie.",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/audit": {
   "get": {
    "summary": "Audit log",
    "description": "Console only (owner or admin); API keys have no scopes, so they can't read it. Query: action (e.g. key or key.created), actor, q, since, until (YYYY-MM-DD), before, limit (≤200), format=csv (up to 10,000 rows).",
    "responses": {
     "200": {
      "description": "OK"
     },
     "default": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "bearer": {
    "type": "http",
    "scheme": "bearer"
   }
  },
  "schemas": {
   "SendEmail": {
    "type": "object",
    "required": [
     "from",
     "to",
     "subject"
    ],
    "properties": {
     "from": {
      "type": "string"
     },
     "to": {
      "oneOf": [
       {
        "type": "string"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ]
     },
     "cc": {
      "oneOf": [
       {
        "type": "string"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ]
     },
     "bcc": {
      "oneOf": [
       {
        "type": "string"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ]
     },
     "reply_to": {
      "oneOf": [
       {
        "type": "string"
       },
       {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      ]
     },
     "subject": {
      "type": "string"
     },
     "html": {
      "type": "string"
     },
     "text": {
      "type": "string"
     },
     "headers": {
      "type": "object",
      "additionalProperties": {
       "type": "string"
      }
     },
     "tags": {
      "type": "object",
      "additionalProperties": {
       "type": "string"
      }
     },
     "scheduled_at": {
      "type": "string",
      "format": "date-time"
     },
     "request_approval": {
      "type": "boolean"
     },
     "attachments": {
      "type": "array",
      "items": {
       "type": "object",
       "required": [
        "filename",
        "content"
       ],
       "properties": {
        "filename": {
         "type": "string"
        },
        "content": {
         "type": "string",
         "contentEncoding": "base64"
        },
        "content_type": {
         "type": "string"
        }
       }
      }
     }
    }
   },
   "Error": {
    "type": "object",
    "properties": {
     "error": {
      "type": "object",
      "properties": {
       "code": {
        "type": "string",
        "enum": [
         "agent_budget_exceeded",
         "plan_daily_limit",
         "plan_monthly_limit",
         "lifetime_pool_used",
         "sending_paused",
         "key_paused",
         "account_paused",
         "domain_not_added",
         "domain_not_verified",
         "invalid_from",
         "invalid_recipient",
         "missing_recipient",
         "too_many_recipients",
         "missing_subject",
         "subject_too_long",
         "missing_content",
         "invalid_header",
         "invalid_attachment",
         "message_too_large",
         "invalid_scheduled_at",
         "invalid_batch",
         "batch_too_large",
         "idempotency_conflict",
         "invalid_idempotency_key",
         "invalid_api_key",
         "unauthorized",
         "console_only",
         "role_forbidden",
         "seat_limit",
         "member_exists",
         "member_not_found",
         "owner_protected",
         "invalid_role",
         "invite_invalid",
         "link_invalid",
         "invalid_email",
         "bad_origin",
         "not_held",
         "not_cancelable",
         "email_not_found",
         "provider_error",
         "invalid_domain",
         "domain_taken",
         "domain_limit",
         "domain_not_found",
         "invalid_webhook_url",
         "invalid_events",
         "consent_required",
         "invalid_contact_email",
         "empty_audience",
         "broadcast_locked",
         "complaint_suppression",
         "inbox_limit",
         "address_taken",
         "username_reserved",
         "invalid_username",
         "no_inbound_domain",
         "catch_all_shared",
         "thread_not_found",
         "message_not_found",
         "inbox_not_found",
         "attachment_not_found",
         "invalid_json",
         "not_found",
         "server_error"
        ]
       },
       "message": {
        "type": "string"
       },
       "fix": {
        "type": "string"
       },
       "docs_url": {
        "type": "string"
       }
      }
     }
    }
   }
  }
 }
}