agentmail-clone-v1 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/.env.example +20 -0
  2. package/.github/workflows/docs-deploy.yml +37 -0
  3. package/.github/workflows/landing-preview.yml +43 -0
  4. package/.github/workflows/openapi-lint.yml +31 -0
  5. package/.github/workflows/sdk-generate-check.yml +66 -0
  6. package/.github/workflows/sdk-release.yml +62 -0
  7. package/CHANGELOG.md +10 -0
  8. package/README.md +208 -0
  9. package/RELEASING.md +43 -0
  10. package/docs/api-reference/api-keys.mdx +11 -0
  11. package/docs/api-reference/domains.mdx +13 -0
  12. package/docs/api-reference/emails.mdx +26 -0
  13. package/docs/api-reference/inboxes.mdx +13 -0
  14. package/docs/api-reference/metrics.mdx +10 -0
  15. package/docs/authentication.mdx +25 -0
  16. package/docs/docs.json +35 -0
  17. package/docs/errors.mdx +34 -0
  18. package/docs/favicon.svg +5 -0
  19. package/docs/idempotency.mdx +18 -0
  20. package/docs/index.mdx +24 -0
  21. package/docs/quickstart.mdx +134 -0
  22. package/landing/DEPLOYING.md +33 -0
  23. package/landing/favicon.svg +5 -0
  24. package/landing/index.html +129 -0
  25. package/landing/main.js +45 -0
  26. package/landing/privacy.html +29 -0
  27. package/landing/styles.css +356 -0
  28. package/landing/terms.html +29 -0
  29. package/netlify.toml +15 -0
  30. package/openapi/openapi.yaml +1016 -0
  31. package/package.json +34 -0
  32. package/render.yaml +48 -0
  33. package/scripts/generate-sdk-py.sh +16 -0
  34. package/scripts/generate-sdk-ts.sh +16 -0
  35. package/scripts/migrate.js +66 -0
  36. package/scripts/validate-docs.js +56 -0
  37. package/scripts/validate-landing.js +39 -0
  38. package/sdks/python/README.md +40 -0
  39. package/sdks/python/emailagent_sdk/__init__.py +157 -0
  40. package/sdks/python/generated/.openapi-generator/FILES +101 -0
  41. package/sdks/python/generated/.openapi-generator/VERSION +1 -0
  42. package/sdks/python/generated/.openapi-generator-ignore +23 -0
  43. package/sdks/python/generated/emailagent_sdk_generated/__init__.py +105 -0
  44. package/sdks/python/generated/emailagent_sdk_generated/api/__init__.py +9 -0
  45. package/sdks/python/generated/emailagent_sdk_generated/api/api_keys_api.py +1162 -0
  46. package/sdks/python/generated/emailagent_sdk_generated/api/domains_api.py +1168 -0
  47. package/sdks/python/generated/emailagent_sdk_generated/api/emails_api.py +1232 -0
  48. package/sdks/python/generated/emailagent_sdk_generated/api/inboxes_api.py +1191 -0
  49. package/sdks/python/generated/emailagent_sdk_generated/api/metrics_api.py +285 -0
  50. package/sdks/python/generated/emailagent_sdk_generated/api_client.py +801 -0
  51. package/sdks/python/generated/emailagent_sdk_generated/api_response.py +21 -0
  52. package/sdks/python/generated/emailagent_sdk_generated/configuration.py +586 -0
  53. package/sdks/python/generated/emailagent_sdk_generated/docs/APIKeysApi.md +334 -0
  54. package/sdks/python/generated/emailagent_sdk_generated/docs/ApiKeyCreated.md +35 -0
  55. package/sdks/python/generated/emailagent_sdk_generated/docs/ApiKeyCreatedResponse.md +29 -0
  56. package/sdks/python/generated/emailagent_sdk_generated/docs/ApiKeyListItem.md +35 -0
  57. package/sdks/python/generated/emailagent_sdk_generated/docs/ApiKeyListResponse.md +29 -0
  58. package/sdks/python/generated/emailagent_sdk_generated/docs/CreateApiKeyRequest.md +30 -0
  59. package/sdks/python/generated/emailagent_sdk_generated/docs/CreateApiKeyRequestScopes.md +29 -0
  60. package/sdks/python/generated/emailagent_sdk_generated/docs/CreateDomainRequest.md +29 -0
  61. package/sdks/python/generated/emailagent_sdk_generated/docs/CreateInboxRequest.md +31 -0
  62. package/sdks/python/generated/emailagent_sdk_generated/docs/Domain.md +37 -0
  63. package/sdks/python/generated/emailagent_sdk_generated/docs/DomainListResponse.md +29 -0
  64. package/sdks/python/generated/emailagent_sdk_generated/docs/DomainResponse.md +29 -0
  65. package/sdks/python/generated/emailagent_sdk_generated/docs/DomainsApi.md +336 -0
  66. package/sdks/python/generated/emailagent_sdk_generated/docs/Email.md +43 -0
  67. package/sdks/python/generated/emailagent_sdk_generated/docs/EmailListResponse.md +29 -0
  68. package/sdks/python/generated/emailagent_sdk_generated/docs/EmailResponse.md +29 -0
  69. package/sdks/python/generated/emailagent_sdk_generated/docs/EmailsApi.md +353 -0
  70. package/sdks/python/generated/emailagent_sdk_generated/docs/ErrorResponse.md +29 -0
  71. package/sdks/python/generated/emailagent_sdk_generated/docs/Inbox.md +38 -0
  72. package/sdks/python/generated/emailagent_sdk_generated/docs/InboxListResponse.md +29 -0
  73. package/sdks/python/generated/emailagent_sdk_generated/docs/InboxResponse.md +29 -0
  74. package/sdks/python/generated/emailagent_sdk_generated/docs/InboxesApi.md +337 -0
  75. package/sdks/python/generated/emailagent_sdk_generated/docs/MetricsApi.md +83 -0
  76. package/sdks/python/generated/emailagent_sdk_generated/docs/MetricsData.md +35 -0
  77. package/sdks/python/generated/emailagent_sdk_generated/docs/MetricsResponse.md +29 -0
  78. package/sdks/python/generated/emailagent_sdk_generated/docs/OkResponse.md +29 -0
  79. package/sdks/python/generated/emailagent_sdk_generated/docs/PlanLimits.md +32 -0
  80. package/sdks/python/generated/emailagent_sdk_generated/docs/SendEmailRequest.md +32 -0
  81. package/sdks/python/generated/emailagent_sdk_generated/docs/UpdateEmailReadRequest.md +29 -0
  82. package/sdks/python/generated/emailagent_sdk_generated/docs/UpdateInboxRequest.md +29 -0
  83. package/sdks/python/generated/emailagent_sdk_generated/exceptions.py +216 -0
  84. package/sdks/python/generated/emailagent_sdk_generated/models/__init__.py +41 -0
  85. package/sdks/python/generated/emailagent_sdk_generated/models/api_key_created.py +113 -0
  86. package/sdks/python/generated/emailagent_sdk_generated/models/api_key_created_response.py +91 -0
  87. package/sdks/python/generated/emailagent_sdk_generated/models/api_key_list_item.py +123 -0
  88. package/sdks/python/generated/emailagent_sdk_generated/models/api_key_list_response.py +95 -0
  89. package/sdks/python/generated/emailagent_sdk_generated/models/create_api_key_request.py +93 -0
  90. package/sdks/python/generated/emailagent_sdk_generated/models/create_api_key_request_scopes.py +143 -0
  91. package/sdks/python/generated/emailagent_sdk_generated/models/create_domain_request.py +87 -0
  92. package/sdks/python/generated/emailagent_sdk_generated/models/create_inbox_request.py +91 -0
  93. package/sdks/python/generated/emailagent_sdk_generated/models/domain.py +134 -0
  94. package/sdks/python/generated/emailagent_sdk_generated/models/domain_list_response.py +95 -0
  95. package/sdks/python/generated/emailagent_sdk_generated/models/domain_response.py +91 -0
  96. package/sdks/python/generated/emailagent_sdk_generated/models/email.py +175 -0
  97. package/sdks/python/generated/emailagent_sdk_generated/models/email_list_response.py +95 -0
  98. package/sdks/python/generated/emailagent_sdk_generated/models/email_response.py +91 -0
  99. package/sdks/python/generated/emailagent_sdk_generated/models/error_response.py +87 -0
  100. package/sdks/python/generated/emailagent_sdk_generated/models/inbox.py +136 -0
  101. package/sdks/python/generated/emailagent_sdk_generated/models/inbox_list_response.py +95 -0
  102. package/sdks/python/generated/emailagent_sdk_generated/models/inbox_response.py +91 -0
  103. package/sdks/python/generated/emailagent_sdk_generated/models/metrics_data.py +110 -0
  104. package/sdks/python/generated/emailagent_sdk_generated/models/metrics_response.py +91 -0
  105. package/sdks/python/generated/emailagent_sdk_generated/models/ok_response.py +87 -0
  106. package/sdks/python/generated/emailagent_sdk_generated/models/plan_limits.py +93 -0
  107. package/sdks/python/generated/emailagent_sdk_generated/models/send_email_request.py +93 -0
  108. package/sdks/python/generated/emailagent_sdk_generated/models/update_email_read_request.py +87 -0
  109. package/sdks/python/generated/emailagent_sdk_generated/models/update_inbox_request.py +92 -0
  110. package/sdks/python/generated/emailagent_sdk_generated/rest.py +258 -0
  111. package/sdks/python/generated/emailagent_sdk_generated/test/__init__.py +0 -0
  112. package/sdks/python/generated/emailagent_sdk_generated/test/test_api_key_created.py +68 -0
  113. package/sdks/python/generated/emailagent_sdk_generated/test/test_api_key_created_response.py +52 -0
  114. package/sdks/python/generated/emailagent_sdk_generated/test/test_api_key_list_item.py +66 -0
  115. package/sdks/python/generated/emailagent_sdk_generated/test/test_api_key_list_response.py +56 -0
  116. package/sdks/python/generated/emailagent_sdk_generated/test/test_api_keys_api.py +59 -0
  117. package/sdks/python/generated/emailagent_sdk_generated/test/test_create_api_key_request.py +53 -0
  118. package/sdks/python/generated/emailagent_sdk_generated/test/test_create_api_key_request_scopes.py +50 -0
  119. package/sdks/python/generated/emailagent_sdk_generated/test/test_create_domain_request.py +52 -0
  120. package/sdks/python/generated/emailagent_sdk_generated/test/test_create_inbox_request.py +54 -0
  121. package/sdks/python/generated/emailagent_sdk_generated/test/test_domain.py +70 -0
  122. package/sdks/python/generated/emailagent_sdk_generated/test/test_domain_list_response.py +56 -0
  123. package/sdks/python/generated/emailagent_sdk_generated/test/test_domain_response.py +52 -0
  124. package/sdks/python/generated/emailagent_sdk_generated/test/test_domains_api.py +59 -0
  125. package/sdks/python/generated/emailagent_sdk_generated/test/test_email.py +79 -0
  126. package/sdks/python/generated/emailagent_sdk_generated/test/test_email_list_response.py +56 -0
  127. package/sdks/python/generated/emailagent_sdk_generated/test/test_email_response.py +52 -0
  128. package/sdks/python/generated/emailagent_sdk_generated/test/test_emails_api.py +59 -0
  129. package/sdks/python/generated/emailagent_sdk_generated/test/test_error_response.py +52 -0
  130. package/sdks/python/generated/emailagent_sdk_generated/test/test_inbox.py +68 -0
  131. package/sdks/python/generated/emailagent_sdk_generated/test/test_inbox_list_response.py +56 -0
  132. package/sdks/python/generated/emailagent_sdk_generated/test/test_inbox_response.py +52 -0
  133. package/sdks/python/generated/emailagent_sdk_generated/test/test_inboxes_api.py +59 -0
  134. package/sdks/python/generated/emailagent_sdk_generated/test/test_metrics_api.py +38 -0
  135. package/sdks/python/generated/emailagent_sdk_generated/test/test_metrics_data.py +72 -0
  136. package/sdks/python/generated/emailagent_sdk_generated/test/test_metrics_response.py +74 -0
  137. package/sdks/python/generated/emailagent_sdk_generated/test/test_ok_response.py +52 -0
  138. package/sdks/python/generated/emailagent_sdk_generated/test/test_plan_limits.py +58 -0
  139. package/sdks/python/generated/emailagent_sdk_generated/test/test_send_email_request.py +56 -0
  140. package/sdks/python/generated/emailagent_sdk_generated/test/test_update_email_read_request.py +52 -0
  141. package/sdks/python/generated/emailagent_sdk_generated/test/test_update_inbox_request.py +52 -0
  142. package/sdks/python/generated/emailagent_sdk_generated_README.md +140 -0
  143. package/sdks/python/openapitools.json +7 -0
  144. package/sdks/python/pyproject.toml +19 -0
  145. package/sdks/typescript/README.md +41 -0
  146. package/sdks/typescript/generated/.openapi-generator/FILES +41 -0
  147. package/sdks/typescript/generated/.openapi-generator/VERSION +1 -0
  148. package/sdks/typescript/generated/.openapi-generator-ignore +23 -0
  149. package/sdks/typescript/generated/package.json +21 -0
  150. package/sdks/typescript/generated/src/apis/APIKeysApi.ts +314 -0
  151. package/sdks/typescript/generated/src/apis/DomainsApi.ts +314 -0
  152. package/sdks/typescript/generated/src/apis/EmailsApi.ts +350 -0
  153. package/sdks/typescript/generated/src/apis/InboxesApi.ts +329 -0
  154. package/sdks/typescript/generated/src/apis/MetricsApi.ts +93 -0
  155. package/sdks/typescript/generated/src/apis/index.ts +7 -0
  156. package/sdks/typescript/generated/src/index.ts +5 -0
  157. package/sdks/typescript/generated/src/models/ApiKeyCreated.ts +123 -0
  158. package/sdks/typescript/generated/src/models/ApiKeyCreatedResponse.ts +74 -0
  159. package/sdks/typescript/generated/src/models/ApiKeyListItem.ts +121 -0
  160. package/sdks/typescript/generated/src/models/ApiKeyListResponse.ts +74 -0
  161. package/sdks/typescript/generated/src/models/CreateApiKeyRequest.ts +82 -0
  162. package/sdks/typescript/generated/src/models/CreateApiKeyRequestScopes.ts +45 -0
  163. package/sdks/typescript/generated/src/models/CreateDomainRequest.ts +66 -0
  164. package/sdks/typescript/generated/src/models/CreateInboxRequest.ts +82 -0
  165. package/sdks/typescript/generated/src/models/Domain.ts +152 -0
  166. package/sdks/typescript/generated/src/models/DomainListResponse.ts +74 -0
  167. package/sdks/typescript/generated/src/models/DomainResponse.ts +74 -0
  168. package/sdks/typescript/generated/src/models/Email.ts +222 -0
  169. package/sdks/typescript/generated/src/models/EmailListResponse.ts +74 -0
  170. package/sdks/typescript/generated/src/models/EmailResponse.ts +74 -0
  171. package/sdks/typescript/generated/src/models/ErrorResponse.ts +66 -0
  172. package/sdks/typescript/generated/src/models/Inbox.ts +159 -0
  173. package/sdks/typescript/generated/src/models/InboxListResponse.ts +74 -0
  174. package/sdks/typescript/generated/src/models/InboxResponse.ts +74 -0
  175. package/sdks/typescript/generated/src/models/MetricsData.ts +139 -0
  176. package/sdks/typescript/generated/src/models/MetricsResponse.ts +74 -0
  177. package/sdks/typescript/generated/src/models/OkResponse.ts +66 -0
  178. package/sdks/typescript/generated/src/models/PlanLimits.ts +93 -0
  179. package/sdks/typescript/generated/src/models/SendEmailRequest.ts +91 -0
  180. package/sdks/typescript/generated/src/models/UpdateEmailReadRequest.ts +66 -0
  181. package/sdks/typescript/generated/src/models/UpdateInboxRequest.ts +66 -0
  182. package/sdks/typescript/generated/src/models/index.ts +27 -0
  183. package/sdks/typescript/generated/src/runtime.ts +432 -0
  184. package/sdks/typescript/generated/tsconfig.esm.json +7 -0
  185. package/sdks/typescript/generated/tsconfig.json +16 -0
  186. package/sdks/typescript/openapitools.json +8 -0
  187. package/sdks/typescript/package.json +27 -0
  188. package/sdks/typescript/src/index.ts +138 -0
  189. package/sdks/typescript/tsconfig.json +14 -0
  190. package/sql/001_init.sql +143 -0
  191. package/sql/002_local_auth.sql +38 -0
  192. package/sql/003_domain_routes.sql +2 -0
  193. package/sql/004_reliability_primitives.sql +75 -0
  194. package/sql/005_auth_email_flows.sql +22 -0
  195. package/src/config.js +30 -0
  196. package/src/db.js +25 -0
  197. package/src/lib/api-auth.js +55 -0
  198. package/src/lib/auth.js +71 -0
  199. package/src/lib/csrf.js +46 -0
  200. package/src/lib/dodo.js +67 -0
  201. package/src/lib/email-templates.js +67 -0
  202. package/src/lib/idempotency.js +85 -0
  203. package/src/lib/mailgun.js +188 -0
  204. package/src/lib/plan.js +24 -0
  205. package/src/lib/rate-limit.js +43 -0
  206. package/src/lib/security.js +62 -0
  207. package/src/lib/session.js +21 -0
  208. package/src/lib/store.js +638 -0
  209. package/src/lib/transactional-mailer.js +54 -0
  210. package/src/lib/validation.js +30 -0
  211. package/src/routes/api.js +485 -0
  212. package/src/routes/app.js +699 -0
  213. package/src/routes/auth.js +404 -0
  214. package/src/routes/webhooks.js +257 -0
  215. package/src/server.js +79 -0
  216. package/src/views/pages/admin.ejs +58 -0
  217. package/src/views/pages/api-keys.ejs +56 -0
  218. package/src/views/pages/billing.ejs +71 -0
  219. package/src/views/pages/domains.ejs +106 -0
  220. package/src/views/pages/inboxes.ejs +127 -0
  221. package/src/views/pages/login.ejs +57 -0
  222. package/src/views/pages/metrics.ejs +34 -0
  223. package/src/views/pages/reset-password.ejs +19 -0
  224. package/src/views/partials/bottom.ejs +3 -0
  225. package/src/views/partials/csrf-field.ejs +3 -0
  226. package/src/views/partials/flash.ejs +3 -0
  227. package/src/views/partials/top.ejs +130 -0
@@ -0,0 +1,1016 @@
1
+ openapi: 3.0.3
2
+ info:
3
+ title: EmailAgent API
4
+ version: 1.0.0
5
+ license:
6
+ name: Proprietary
7
+ url: https://emailagent.dev/legal/terms
8
+ description: |
9
+ API for creating agent inboxes, sending emails, managing custom domains,
10
+ managing API keys, and reading usage metrics.
11
+
12
+ Authentication uses API keys via `Authorization: Bearer <api_key>`.
13
+
14
+ For mutating endpoints, send an optional `Idempotency-Key` header.
15
+ - Replaying the same key with the same payload returns the original response.
16
+ - Reusing a key with a different payload returns `409`.
17
+ servers:
18
+ - url: http://127.0.0.1:3000
19
+ description: Local
20
+ - url: https://api.emailagent.dev
21
+ description: Production
22
+ tags:
23
+ - name: Inboxes
24
+ description: Manage organization inboxes.
25
+ - name: Emails
26
+ description: Send and manage inbox email records.
27
+ - name: Domains
28
+ description: Create, verify, and delete custom domains.
29
+ - name: API Keys
30
+ description: Create and manage API keys and scopes.
31
+ - name: Metrics
32
+ description: Read plan usage metrics.
33
+ security:
34
+ - bearerAuth: []
35
+ paths:
36
+ /api/v1/inboxes:
37
+ get:
38
+ tags: [Inboxes]
39
+ summary: List inboxes
40
+ operationId: listInboxes
41
+ responses:
42
+ '200':
43
+ description: Inboxes for the authenticated organization
44
+ content:
45
+ application/json:
46
+ schema:
47
+ $ref: '#/components/schemas/InboxListResponse'
48
+ '401':
49
+ $ref: '#/components/responses/Unauthorized'
50
+ '403':
51
+ $ref: '#/components/responses/Forbidden'
52
+ post:
53
+ tags: [Inboxes]
54
+ summary: Create inbox
55
+ operationId: createInbox
56
+ parameters:
57
+ - $ref: '#/components/parameters/IdempotencyKey'
58
+ requestBody:
59
+ required: true
60
+ content:
61
+ application/json:
62
+ schema:
63
+ $ref: '#/components/schemas/CreateInboxRequest'
64
+ responses:
65
+ '201':
66
+ description: Inbox created
67
+ content:
68
+ application/json:
69
+ schema:
70
+ $ref: '#/components/schemas/InboxResponse'
71
+ '400':
72
+ $ref: '#/components/responses/BadRequest'
73
+ '401':
74
+ $ref: '#/components/responses/Unauthorized'
75
+ '403':
76
+ $ref: '#/components/responses/Forbidden'
77
+ '409':
78
+ $ref: '#/components/responses/Conflict'
79
+ '422':
80
+ description: Plan limit exceeded
81
+ content:
82
+ application/json:
83
+ schema:
84
+ $ref: '#/components/schemas/ErrorResponse'
85
+
86
+ /api/v1/inboxes/{id}:
87
+ patch:
88
+ tags: [Inboxes]
89
+ summary: Update inbox display name
90
+ operationId: updateInbox
91
+ parameters:
92
+ - $ref: '#/components/parameters/InboxId'
93
+ - $ref: '#/components/parameters/IdempotencyKey'
94
+ requestBody:
95
+ required: true
96
+ content:
97
+ application/json:
98
+ schema:
99
+ $ref: '#/components/schemas/UpdateInboxRequest'
100
+ responses:
101
+ '200':
102
+ description: Inbox updated
103
+ content:
104
+ application/json:
105
+ schema:
106
+ $ref: '#/components/schemas/InboxResponse'
107
+ '401':
108
+ $ref: '#/components/responses/Unauthorized'
109
+ '403':
110
+ $ref: '#/components/responses/Forbidden'
111
+ '404':
112
+ description: Inbox not found
113
+ content:
114
+ application/json:
115
+ schema:
116
+ $ref: '#/components/schemas/ErrorResponse'
117
+ '409':
118
+ $ref: '#/components/responses/Conflict'
119
+ delete:
120
+ tags: [Inboxes]
121
+ summary: Delete inbox
122
+ operationId: deleteInbox
123
+ parameters:
124
+ - $ref: '#/components/parameters/InboxId'
125
+ - $ref: '#/components/parameters/IdempotencyKey'
126
+ responses:
127
+ '200':
128
+ description: Inbox deleted
129
+ content:
130
+ application/json:
131
+ schema:
132
+ $ref: '#/components/schemas/OkResponse'
133
+ '401':
134
+ $ref: '#/components/responses/Unauthorized'
135
+ '403':
136
+ $ref: '#/components/responses/Forbidden'
137
+ '404':
138
+ description: Inbox not found
139
+ content:
140
+ application/json:
141
+ schema:
142
+ $ref: '#/components/schemas/ErrorResponse'
143
+ '409':
144
+ $ref: '#/components/responses/Conflict'
145
+
146
+ /api/v1/emails/send:
147
+ post:
148
+ tags: [Emails]
149
+ summary: Send outbound email from an inbox
150
+ operationId: sendEmail
151
+ description: Requires `emails:send` scope.
152
+ parameters:
153
+ - $ref: '#/components/parameters/IdempotencyKey'
154
+ requestBody:
155
+ required: true
156
+ content:
157
+ application/json:
158
+ schema:
159
+ $ref: '#/components/schemas/SendEmailRequest'
160
+ responses:
161
+ '201':
162
+ description: Email send accepted
163
+ content:
164
+ application/json:
165
+ schema:
166
+ $ref: '#/components/schemas/EmailResponse'
167
+ '400':
168
+ $ref: '#/components/responses/BadRequest'
169
+ '401':
170
+ $ref: '#/components/responses/Unauthorized'
171
+ '403':
172
+ $ref: '#/components/responses/Forbidden'
173
+ '409':
174
+ $ref: '#/components/responses/Conflict'
175
+ '422':
176
+ description: Plan limit exceeded
177
+ content:
178
+ application/json:
179
+ schema:
180
+ $ref: '#/components/schemas/ErrorResponse'
181
+ '429':
182
+ description: Rate limit exceeded
183
+ content:
184
+ application/json:
185
+ schema:
186
+ $ref: '#/components/schemas/ErrorResponse'
187
+ '500':
188
+ description: Provider or internal error
189
+ content:
190
+ application/json:
191
+ schema:
192
+ $ref: '#/components/schemas/ErrorResponse'
193
+
194
+ /api/v1/emails:
195
+ get:
196
+ tags: [Emails]
197
+ summary: List emails
198
+ operationId: listEmails
199
+ description: Returns inbound and outbound email records. Requires `emails:read` scope.
200
+ parameters:
201
+ - in: query
202
+ name: inboxId
203
+ required: false
204
+ schema:
205
+ type: string
206
+ format: uuid
207
+ description: Optional inbox filter.
208
+ responses:
209
+ '200':
210
+ description: Email records for the authenticated organization
211
+ content:
212
+ application/json:
213
+ schema:
214
+ $ref: '#/components/schemas/EmailListResponse'
215
+ '400':
216
+ $ref: '#/components/responses/BadRequest'
217
+ '401':
218
+ $ref: '#/components/responses/Unauthorized'
219
+ '403':
220
+ $ref: '#/components/responses/Forbidden'
221
+
222
+ /api/v1/emails/{id}/read:
223
+ patch:
224
+ tags: [Emails]
225
+ summary: Update email read state
226
+ operationId: updateEmailRead
227
+ description: Marks an email as read/unread. Requires `emails:write` scope.
228
+ parameters:
229
+ - $ref: '#/components/parameters/EmailId'
230
+ - $ref: '#/components/parameters/IdempotencyKey'
231
+ requestBody:
232
+ required: true
233
+ content:
234
+ application/json:
235
+ schema:
236
+ $ref: '#/components/schemas/UpdateEmailReadRequest'
237
+ responses:
238
+ '200':
239
+ description: Email updated
240
+ content:
241
+ application/json:
242
+ schema:
243
+ $ref: '#/components/schemas/EmailResponse'
244
+ '400':
245
+ $ref: '#/components/responses/BadRequest'
246
+ '401':
247
+ $ref: '#/components/responses/Unauthorized'
248
+ '403':
249
+ $ref: '#/components/responses/Forbidden'
250
+ '404':
251
+ description: Email not found
252
+ content:
253
+ application/json:
254
+ schema:
255
+ $ref: '#/components/schemas/ErrorResponse'
256
+ '409':
257
+ $ref: '#/components/responses/Conflict'
258
+
259
+ /api/v1/emails/{id}:
260
+ delete:
261
+ tags: [Emails]
262
+ summary: Delete email
263
+ operationId: deleteEmail
264
+ description: Soft-deletes an email record. Requires `emails:write` scope.
265
+ parameters:
266
+ - $ref: '#/components/parameters/EmailId'
267
+ - $ref: '#/components/parameters/IdempotencyKey'
268
+ responses:
269
+ '200':
270
+ description: Email deleted
271
+ content:
272
+ application/json:
273
+ schema:
274
+ $ref: '#/components/schemas/OkResponse'
275
+ '401':
276
+ $ref: '#/components/responses/Unauthorized'
277
+ '403':
278
+ $ref: '#/components/responses/Forbidden'
279
+ '404':
280
+ description: Email not found
281
+ content:
282
+ application/json:
283
+ schema:
284
+ $ref: '#/components/schemas/ErrorResponse'
285
+ '409':
286
+ $ref: '#/components/responses/Conflict'
287
+
288
+ /api/v1/domains:
289
+ get:
290
+ tags: [Domains]
291
+ summary: List custom domains
292
+ operationId: listDomains
293
+ responses:
294
+ '200':
295
+ description: Domains for the authenticated organization
296
+ content:
297
+ application/json:
298
+ schema:
299
+ $ref: '#/components/schemas/DomainListResponse'
300
+ '401':
301
+ $ref: '#/components/responses/Unauthorized'
302
+ '403':
303
+ $ref: '#/components/responses/Forbidden'
304
+ post:
305
+ tags: [Domains]
306
+ summary: Create custom domain
307
+ operationId: createDomain
308
+ parameters:
309
+ - $ref: '#/components/parameters/IdempotencyKey'
310
+ requestBody:
311
+ required: true
312
+ content:
313
+ application/json:
314
+ schema:
315
+ $ref: '#/components/schemas/CreateDomainRequest'
316
+ responses:
317
+ '201':
318
+ description: Domain created
319
+ content:
320
+ application/json:
321
+ schema:
322
+ $ref: '#/components/schemas/DomainResponse'
323
+ '400':
324
+ $ref: '#/components/responses/BadRequest'
325
+ '401':
326
+ $ref: '#/components/responses/Unauthorized'
327
+ '403':
328
+ $ref: '#/components/responses/Forbidden'
329
+ '409':
330
+ description: Domain already exists or idempotency conflict
331
+ content:
332
+ application/json:
333
+ schema:
334
+ $ref: '#/components/schemas/ErrorResponse'
335
+ '422':
336
+ description: Plan does not allow custom domains or limit exceeded
337
+ content:
338
+ application/json:
339
+ schema:
340
+ $ref: '#/components/schemas/ErrorResponse'
341
+ '500':
342
+ description: Provider or internal error
343
+ content:
344
+ application/json:
345
+ schema:
346
+ $ref: '#/components/schemas/ErrorResponse'
347
+
348
+ /api/v1/domains/{id}/verify:
349
+ post:
350
+ tags: [Domains]
351
+ summary: Refresh DNS verification status for a domain
352
+ operationId: verifyDomain
353
+ parameters:
354
+ - $ref: '#/components/parameters/DomainId'
355
+ - $ref: '#/components/parameters/IdempotencyKey'
356
+ responses:
357
+ '200':
358
+ description: Domain verification refreshed
359
+ content:
360
+ application/json:
361
+ schema:
362
+ $ref: '#/components/schemas/DomainResponse'
363
+ '401':
364
+ $ref: '#/components/responses/Unauthorized'
365
+ '403':
366
+ $ref: '#/components/responses/Forbidden'
367
+ '404':
368
+ description: Domain not found
369
+ content:
370
+ application/json:
371
+ schema:
372
+ $ref: '#/components/schemas/ErrorResponse'
373
+ '409':
374
+ $ref: '#/components/responses/Conflict'
375
+ '500':
376
+ description: Provider or internal error
377
+ content:
378
+ application/json:
379
+ schema:
380
+ $ref: '#/components/schemas/ErrorResponse'
381
+
382
+ /api/v1/domains/{id}:
383
+ delete:
384
+ tags: [Domains]
385
+ summary: Delete custom domain
386
+ operationId: deleteDomain
387
+ parameters:
388
+ - $ref: '#/components/parameters/DomainId'
389
+ - $ref: '#/components/parameters/IdempotencyKey'
390
+ responses:
391
+ '200':
392
+ description: Domain deleted
393
+ content:
394
+ application/json:
395
+ schema:
396
+ $ref: '#/components/schemas/OkResponse'
397
+ '401':
398
+ $ref: '#/components/responses/Unauthorized'
399
+ '403':
400
+ $ref: '#/components/responses/Forbidden'
401
+ '404':
402
+ description: Domain not found
403
+ content:
404
+ application/json:
405
+ schema:
406
+ $ref: '#/components/schemas/ErrorResponse'
407
+ '409':
408
+ $ref: '#/components/responses/Conflict'
409
+
410
+ /api/v1/api-keys:
411
+ get:
412
+ tags: [API Keys]
413
+ summary: List API keys
414
+ operationId: listApiKeys
415
+ responses:
416
+ '200':
417
+ description: API keys for the organization
418
+ content:
419
+ application/json:
420
+ schema:
421
+ $ref: '#/components/schemas/ApiKeyListResponse'
422
+ '401':
423
+ $ref: '#/components/responses/Unauthorized'
424
+ '403':
425
+ $ref: '#/components/responses/Forbidden'
426
+ post:
427
+ tags: [API Keys]
428
+ summary: Create API key
429
+ operationId: createApiKey
430
+ parameters:
431
+ - $ref: '#/components/parameters/IdempotencyKey'
432
+ requestBody:
433
+ required: true
434
+ content:
435
+ application/json:
436
+ schema:
437
+ $ref: '#/components/schemas/CreateApiKeyRequest'
438
+ responses:
439
+ '201':
440
+ description: API key created (secret shown once)
441
+ content:
442
+ application/json:
443
+ schema:
444
+ $ref: '#/components/schemas/ApiKeyCreatedResponse'
445
+ '400':
446
+ $ref: '#/components/responses/BadRequest'
447
+ '401':
448
+ $ref: '#/components/responses/Unauthorized'
449
+ '403':
450
+ $ref: '#/components/responses/Forbidden'
451
+ '409':
452
+ $ref: '#/components/responses/Conflict'
453
+ '422':
454
+ description: Plan key limit exceeded
455
+ content:
456
+ application/json:
457
+ schema:
458
+ $ref: '#/components/schemas/ErrorResponse'
459
+
460
+ /api/v1/api-keys/{id}/revoke:
461
+ post:
462
+ tags: [API Keys]
463
+ summary: Revoke API key
464
+ operationId: revokeApiKey
465
+ parameters:
466
+ - $ref: '#/components/parameters/ApiKeyId'
467
+ - $ref: '#/components/parameters/IdempotencyKey'
468
+ responses:
469
+ '200':
470
+ description: API key revoked
471
+ content:
472
+ application/json:
473
+ schema:
474
+ $ref: '#/components/schemas/OkResponse'
475
+ '401':
476
+ $ref: '#/components/responses/Unauthorized'
477
+ '403':
478
+ $ref: '#/components/responses/Forbidden'
479
+ '404':
480
+ description: API key not found
481
+ content:
482
+ application/json:
483
+ schema:
484
+ $ref: '#/components/schemas/ErrorResponse'
485
+ '409':
486
+ $ref: '#/components/responses/Conflict'
487
+
488
+ /api/v1/api-keys/{id}/rotate:
489
+ post:
490
+ tags: [API Keys]
491
+ summary: Rotate API key (creates new key and revokes old key)
492
+ operationId: rotateApiKey
493
+ parameters:
494
+ - $ref: '#/components/parameters/ApiKeyId'
495
+ - $ref: '#/components/parameters/IdempotencyKey'
496
+ responses:
497
+ '201':
498
+ description: API key rotated (new secret shown once)
499
+ content:
500
+ application/json:
501
+ schema:
502
+ $ref: '#/components/schemas/ApiKeyCreatedResponse'
503
+ '401':
504
+ $ref: '#/components/responses/Unauthorized'
505
+ '403':
506
+ $ref: '#/components/responses/Forbidden'
507
+ '404':
508
+ description: API key not found
509
+ content:
510
+ application/json:
511
+ schema:
512
+ $ref: '#/components/schemas/ErrorResponse'
513
+ '409':
514
+ $ref: '#/components/responses/Conflict'
515
+
516
+ /api/v1/metrics:
517
+ get:
518
+ tags: [Metrics]
519
+ summary: Get organization metrics snapshot
520
+ operationId: getMetrics
521
+ responses:
522
+ '200':
523
+ description: Current month metrics and plan limits
524
+ content:
525
+ application/json:
526
+ schema:
527
+ $ref: '#/components/schemas/MetricsResponse'
528
+ '401':
529
+ $ref: '#/components/responses/Unauthorized'
530
+ '403':
531
+ $ref: '#/components/responses/Forbidden'
532
+
533
+ components:
534
+ securitySchemes:
535
+ bearerAuth:
536
+ type: http
537
+ scheme: bearer
538
+ bearerFormat: APIKey
539
+
540
+ parameters:
541
+ IdempotencyKey:
542
+ in: header
543
+ name: Idempotency-Key
544
+ required: false
545
+ schema:
546
+ type: string
547
+ minLength: 1
548
+ maxLength: 255
549
+ description: Optional key for safe retries of mutating operations.
550
+ InboxId:
551
+ in: path
552
+ name: id
553
+ required: true
554
+ schema:
555
+ type: string
556
+ format: uuid
557
+ DomainId:
558
+ in: path
559
+ name: id
560
+ required: true
561
+ schema:
562
+ type: string
563
+ format: uuid
564
+ EmailId:
565
+ in: path
566
+ name: id
567
+ required: true
568
+ schema:
569
+ type: string
570
+ format: uuid
571
+ ApiKeyId:
572
+ in: path
573
+ name: id
574
+ required: true
575
+ schema:
576
+ type: string
577
+ format: uuid
578
+
579
+ responses:
580
+ Unauthorized:
581
+ description: Missing or invalid bearer token
582
+ content:
583
+ application/json:
584
+ schema:
585
+ $ref: '#/components/schemas/ErrorResponse'
586
+ example:
587
+ error: Missing bearer token
588
+ Forbidden:
589
+ description: API key lacks required scope
590
+ content:
591
+ application/json:
592
+ schema:
593
+ $ref: '#/components/schemas/ErrorResponse'
594
+ example:
595
+ error: "Missing scope: inboxes:write"
596
+ BadRequest:
597
+ description: Validation error
598
+ content:
599
+ application/json:
600
+ schema:
601
+ $ref: '#/components/schemas/ErrorResponse'
602
+ Conflict:
603
+ description: Idempotency conflict or request in progress
604
+ content:
605
+ application/json:
606
+ schema:
607
+ $ref: '#/components/schemas/ErrorResponse'
608
+ examples:
609
+ differentPayload:
610
+ value:
611
+ error: Idempotency key reuse with different payload
612
+ inProgress:
613
+ value:
614
+ error: Request with this idempotency key is already in progress
615
+
616
+ schemas:
617
+ ErrorResponse:
618
+ type: object
619
+ additionalProperties: false
620
+ required: [error]
621
+ properties:
622
+ error:
623
+ type: string
624
+
625
+ OkResponse:
626
+ type: object
627
+ additionalProperties: false
628
+ required: [ok]
629
+ properties:
630
+ ok:
631
+ type: boolean
632
+ enum: [true]
633
+
634
+ Inbox:
635
+ type: object
636
+ additionalProperties: true
637
+ required:
638
+ - id
639
+ - org_id
640
+ - local_part
641
+ - domain_name
642
+ - email_address
643
+ - status
644
+ - created_at
645
+ - updated_at
646
+ properties:
647
+ id:
648
+ type: string
649
+ format: uuid
650
+ org_id:
651
+ type: string
652
+ format: uuid
653
+ local_part:
654
+ type: string
655
+ domain_name:
656
+ type: string
657
+ email_address:
658
+ type: string
659
+ format: email
660
+ display_name:
661
+ type: string
662
+ nullable: true
663
+ status:
664
+ type: string
665
+ enum: [active, deleted]
666
+ created_at:
667
+ type: string
668
+ format: date-time
669
+ updated_at:
670
+ type: string
671
+ format: date-time
672
+ deleted_at:
673
+ type: string
674
+ format: date-time
675
+ nullable: true
676
+
677
+ InboxListResponse:
678
+ type: object
679
+ required: [data]
680
+ properties:
681
+ data:
682
+ type: array
683
+ items:
684
+ $ref: '#/components/schemas/Inbox'
685
+
686
+ InboxResponse:
687
+ type: object
688
+ required: [data]
689
+ properties:
690
+ data:
691
+ $ref: '#/components/schemas/Inbox'
692
+
693
+ CreateInboxRequest:
694
+ type: object
695
+ additionalProperties: false
696
+ required: [localPart]
697
+ properties:
698
+ localPart:
699
+ type: string
700
+ description: Local part only (e.g. `agent-1`)
701
+ displayName:
702
+ type: string
703
+ domainName:
704
+ type: string
705
+ description: Optional domain. Defaults to shared app domain.
706
+
707
+ UpdateInboxRequest:
708
+ type: object
709
+ additionalProperties: false
710
+ required: [displayName]
711
+ properties:
712
+ displayName:
713
+ type: string
714
+ nullable: true
715
+
716
+ Email:
717
+ type: object
718
+ additionalProperties: true
719
+ required:
720
+ - id
721
+ - org_id
722
+ - direction
723
+ - delivery_status
724
+ - subject
725
+ - from_address
726
+ - to_addresses
727
+ - is_read
728
+ - status
729
+ - created_at
730
+ properties:
731
+ id:
732
+ type: string
733
+ format: uuid
734
+ org_id:
735
+ type: string
736
+ format: uuid
737
+ inbox_id:
738
+ type: string
739
+ format: uuid
740
+ nullable: true
741
+ direction:
742
+ type: string
743
+ enum: [inbound, outbound]
744
+ delivery_status:
745
+ type: string
746
+ enum: [unknown, queued, sent, failed, received]
747
+ message_id:
748
+ type: string
749
+ nullable: true
750
+ subject:
751
+ type: string
752
+ from_address:
753
+ type: string
754
+ to_addresses:
755
+ type: array
756
+ items:
757
+ type: string
758
+ text_body:
759
+ type: string
760
+ nullable: true
761
+ html_body:
762
+ type: string
763
+ nullable: true
764
+ is_read:
765
+ type: boolean
766
+ status:
767
+ type: string
768
+ enum: [active, deleted]
769
+ provider_payload:
770
+ type: object
771
+ nullable: true
772
+ additionalProperties: true
773
+ created_at:
774
+ type: string
775
+ format: date-time
776
+
777
+ EmailResponse:
778
+ type: object
779
+ required: [data]
780
+ properties:
781
+ data:
782
+ $ref: '#/components/schemas/Email'
783
+
784
+ EmailListResponse:
785
+ type: object
786
+ required: [data]
787
+ properties:
788
+ data:
789
+ type: array
790
+ items:
791
+ $ref: '#/components/schemas/Email'
792
+
793
+ SendEmailRequest:
794
+ type: object
795
+ additionalProperties: false
796
+ required: [inboxId, to]
797
+ properties:
798
+ inboxId:
799
+ type: string
800
+ format: uuid
801
+ to:
802
+ type: string
803
+ description: Comma-separated recipient emails
804
+ example: "alice@example.com,bob@example.com"
805
+ subject:
806
+ type: string
807
+ text:
808
+ type: string
809
+
810
+ UpdateEmailReadRequest:
811
+ type: object
812
+ additionalProperties: false
813
+ required: [isRead]
814
+ properties:
815
+ isRead:
816
+ type: boolean
817
+
818
+ Domain:
819
+ type: object
820
+ additionalProperties: true
821
+ required:
822
+ - id
823
+ - org_id
824
+ - name
825
+ - status
826
+ - dns_records_json
827
+ - created_at
828
+ - updated_at
829
+ properties:
830
+ id:
831
+ type: string
832
+ format: uuid
833
+ org_id:
834
+ type: string
835
+ format: uuid
836
+ name:
837
+ type: string
838
+ provider_domain_id:
839
+ type: string
840
+ nullable: true
841
+ provider_route_id:
842
+ type: string
843
+ nullable: true
844
+ status:
845
+ type: string
846
+ enum: [pending, active, failed, deleted]
847
+ dns_records_json:
848
+ type: array
849
+ items:
850
+ type: object
851
+ additionalProperties: true
852
+ created_at:
853
+ type: string
854
+ format: date-time
855
+ updated_at:
856
+ type: string
857
+ format: date-time
858
+
859
+ DomainResponse:
860
+ type: object
861
+ required: [data]
862
+ properties:
863
+ data:
864
+ $ref: '#/components/schemas/Domain'
865
+
866
+ DomainListResponse:
867
+ type: object
868
+ required: [data]
869
+ properties:
870
+ data:
871
+ type: array
872
+ items:
873
+ $ref: '#/components/schemas/Domain'
874
+
875
+ CreateDomainRequest:
876
+ type: object
877
+ additionalProperties: false
878
+ required: [name]
879
+ properties:
880
+ name:
881
+ type: string
882
+ description: Domain to onboard, e.g. mail.example.com
883
+
884
+ CreateApiKeyRequest:
885
+ type: object
886
+ additionalProperties: false
887
+ required: [name]
888
+ properties:
889
+ name:
890
+ type: string
891
+ scopes:
892
+ description: Comma-separated scopes string or array of scopes
893
+ oneOf:
894
+ - type: string
895
+ - type: array
896
+ items:
897
+ type: string
898
+
899
+ ApiKeyListItem:
900
+ type: object
901
+ additionalProperties: true
902
+ required: [id, name, key_prefix, scopes, created_at]
903
+ properties:
904
+ id:
905
+ type: string
906
+ format: uuid
907
+ name:
908
+ type: string
909
+ key_prefix:
910
+ type: string
911
+ scopes:
912
+ type: array
913
+ items:
914
+ type: string
915
+ last_used_at:
916
+ type: string
917
+ format: date-time
918
+ nullable: true
919
+ revoked_at:
920
+ type: string
921
+ format: date-time
922
+ nullable: true
923
+ created_at:
924
+ type: string
925
+ format: date-time
926
+
927
+ ApiKeyCreated:
928
+ type: object
929
+ additionalProperties: true
930
+ required: [id, org_id, name, key_prefix, scopes, created_at, secret]
931
+ properties:
932
+ id:
933
+ type: string
934
+ format: uuid
935
+ org_id:
936
+ type: string
937
+ format: uuid
938
+ name:
939
+ type: string
940
+ key_prefix:
941
+ type: string
942
+ scopes:
943
+ type: array
944
+ items:
945
+ type: string
946
+ created_at:
947
+ type: string
948
+ format: date-time
949
+ secret:
950
+ type: string
951
+ description: Full API key value, only returned on create/rotate
952
+
953
+ ApiKeyListResponse:
954
+ type: object
955
+ required: [data]
956
+ properties:
957
+ data:
958
+ type: array
959
+ items:
960
+ $ref: '#/components/schemas/ApiKeyListItem'
961
+
962
+ ApiKeyCreatedResponse:
963
+ type: object
964
+ required: [data]
965
+ properties:
966
+ data:
967
+ $ref: '#/components/schemas/ApiKeyCreated'
968
+
969
+ PlanLimits:
970
+ type: object
971
+ additionalProperties: false
972
+ required: [maxInboxes, maxCustomDomains, maxApiKeys, monthlyEmails]
973
+ properties:
974
+ maxInboxes:
975
+ type: integer
976
+ maxCustomDomains:
977
+ type: integer
978
+ maxApiKeys:
979
+ type: integer
980
+ monthlyEmails:
981
+ type: integer
982
+
983
+ MetricsData:
984
+ type: object
985
+ additionalProperties: false
986
+ required:
987
+ - sentCount
988
+ - inboundCount
989
+ - inboxCount
990
+ - customDomainCount
991
+ - apiKeyCount
992
+ - limits
993
+ - plan
994
+ properties:
995
+ sentCount:
996
+ type: integer
997
+ inboundCount:
998
+ type: integer
999
+ inboxCount:
1000
+ type: integer
1001
+ customDomainCount:
1002
+ type: integer
1003
+ apiKeyCount:
1004
+ type: integer
1005
+ plan:
1006
+ type: string
1007
+ enum: [free, paid]
1008
+ limits:
1009
+ $ref: '#/components/schemas/PlanLimits'
1010
+
1011
+ MetricsResponse:
1012
+ type: object
1013
+ required: [data]
1014
+ properties:
1015
+ data:
1016
+ $ref: '#/components/schemas/MetricsData'