@primitivedotdev/sdk 0.2.4 → 0.3.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 (41) hide show
  1. package/README.md +65 -2
  2. package/bin/run.js +5 -0
  3. package/dist/api/generated/client/client.gen.js +235 -0
  4. package/dist/api/generated/client/index.js +6 -0
  5. package/dist/api/generated/client/types.gen.js +2 -0
  6. package/dist/api/generated/client/utils.gen.js +228 -0
  7. package/dist/api/generated/client.gen.js +3 -0
  8. package/dist/api/generated/core/auth.gen.js +14 -0
  9. package/dist/api/generated/core/bodySerializer.gen.js +57 -0
  10. package/dist/api/generated/core/params.gen.js +100 -0
  11. package/dist/api/generated/core/pathSerializer.gen.js +106 -0
  12. package/dist/api/generated/core/queryKeySerializer.gen.js +92 -0
  13. package/dist/api/generated/core/serverSentEvents.gen.js +132 -0
  14. package/dist/api/generated/core/types.gen.js +2 -0
  15. package/dist/api/generated/core/utils.gen.js +87 -0
  16. package/dist/api/generated/index.js +2 -0
  17. package/dist/api/generated/sdk.gen.js +344 -0
  18. package/dist/api/generated/types.gen.js +2 -0
  19. package/dist/api/index.d.ts +1832 -0
  20. package/dist/api/index.js +39 -0
  21. package/dist/chunk-Cl8Af3a2.js +11 -0
  22. package/dist/contract/index.d.ts +3 -3
  23. package/dist/contract/index.js +2 -2
  24. package/dist/{index-BdRIHaXz.d.ts → index-D2OuDGVz.d.ts} +81 -5
  25. package/dist/index.d.ts +3 -3
  26. package/dist/index.js +2 -2
  27. package/dist/oclif/api-command.js +154 -0
  28. package/dist/oclif/fish-completion.js +87 -0
  29. package/dist/oclif/index.js +42 -0
  30. package/dist/openapi/index.d.ts +41 -0
  31. package/dist/openapi/index.js +8 -0
  32. package/dist/openapi/openapi.generated.js +2644 -0
  33. package/dist/openapi/operations.generated.js +632 -0
  34. package/dist/parser/index.d.ts +1 -1
  35. package/dist/parser/index.js +40 -25
  36. package/dist/{types-B5IgP-Zx.d.ts → types-C3ms4R0d.d.ts} +4 -0
  37. package/dist/webhook/index.d.ts +3 -3
  38. package/dist/webhook/index.js +2 -2
  39. package/dist/{webhook-Be2vM0F-.js → webhook-uSco6pyX.js} +176 -11
  40. package/oclif.manifest.json +1255 -0
  41. package/package.json +81 -13
@@ -0,0 +1,2644 @@
1
+ /**
2
+ * OpenAPI document for the Primitive API.
3
+ *
4
+ * AUTO-GENERATED - DO NOT EDIT
5
+ * Run `pnpm generate:openapi` to regenerate.
6
+ */
7
+ export const openapiDocument = {
8
+ "openapi": "3.1.0",
9
+ "info": {
10
+ "title": "Primitive API",
11
+ "version": "1.0.0",
12
+ "description": "The Primitive API lets you manage domains, emails, webhook endpoints,\nfilters, and account settings programmatically.\n\n## Authentication\n\nAll endpoints require a Bearer token in the `Authorization` header:\n\n```\nAuthorization: Bearer prim_<your_api_key>\n```\n\nAPI keys are org-scoped. Create and manage them in your dashboard\nunder Settings > API Keys.\n\n## Rate Limiting\n\nThe API enforces a sliding window rate limit of **120 requests per\n60 seconds** per organization. When exceeded, the API returns `429`\nwith a `Retry-After` header indicating how many seconds to wait.\n\n## Pagination\n\nList endpoints use cursor-based pagination. Responses include a\n`meta` object with `total`, `limit`, and `cursor` fields. Pass the\n`cursor` value as a query parameter to fetch the next page. When\n`cursor` is `null`, there are no more results.\n\n## Response Format\n\nAll responses use a consistent envelope:\n\n```json\n{\n \"success\": true,\n \"data\": { ... },\n \"meta\": { \"total\": 42, \"limit\": 50, \"cursor\": \"...\" }\n}\n```\n\nErrors follow the same pattern:\n\n```json\n{\n \"success\": false,\n \"error\": { \"code\": \"not_found\", \"message\": \"Email not found\" }\n}\n```\n",
13
+ "contact": {
14
+ "name": "Primitive",
15
+ "url": "https://primitive.dev"
16
+ },
17
+ "license": {
18
+ "name": "Proprietary",
19
+ "url": "https://primitive.dev/terms"
20
+ }
21
+ },
22
+ "servers": [
23
+ {
24
+ "url": "https://www.primitive.dev/api/v1",
25
+ "description": "Production"
26
+ }
27
+ ],
28
+ "security": [
29
+ {
30
+ "BearerAuth": []
31
+ }
32
+ ],
33
+ "tags": [
34
+ {
35
+ "name": "Account",
36
+ "description": "Manage your account settings, storage, and webhook secret"
37
+ },
38
+ {
39
+ "name": "Domains",
40
+ "description": "Claim, verify, and manage email domains"
41
+ },
42
+ {
43
+ "name": "Emails",
44
+ "description": "List, inspect, and manage received emails"
45
+ },
46
+ {
47
+ "name": "Endpoints",
48
+ "description": "Manage webhook endpoints that receive email events"
49
+ },
50
+ {
51
+ "name": "Filters",
52
+ "description": "Manage whitelist and blocklist filter rules"
53
+ },
54
+ {
55
+ "name": "Webhook Deliveries",
56
+ "description": "View and replay webhook delivery attempts"
57
+ }
58
+ ],
59
+ "paths": {
60
+ "/account": {
61
+ "get": {
62
+ "operationId": "getAccount",
63
+ "summary": "Get account info",
64
+ "tags": [
65
+ "Account"
66
+ ],
67
+ "responses": {
68
+ "200": {
69
+ "description": "Account details",
70
+ "content": {
71
+ "application/json": {
72
+ "schema": {
73
+ "allOf": [
74
+ {
75
+ "$ref": "#/components/schemas/SuccessEnvelope"
76
+ },
77
+ {
78
+ "type": "object",
79
+ "properties": {
80
+ "data": {
81
+ "$ref": "#/components/schemas/Account"
82
+ }
83
+ }
84
+ }
85
+ ]
86
+ }
87
+ }
88
+ }
89
+ },
90
+ "401": {
91
+ "$ref": "#/components/responses/Unauthorized"
92
+ },
93
+ "404": {
94
+ "$ref": "#/components/responses/NotFound"
95
+ }
96
+ }
97
+ },
98
+ "patch": {
99
+ "operationId": "updateAccount",
100
+ "summary": "Update account settings",
101
+ "tags": [
102
+ "Account"
103
+ ],
104
+ "requestBody": {
105
+ "required": true,
106
+ "content": {
107
+ "application/json": {
108
+ "schema": {
109
+ "$ref": "#/components/schemas/UpdateAccountInput"
110
+ }
111
+ }
112
+ }
113
+ },
114
+ "responses": {
115
+ "200": {
116
+ "description": "Updated account",
117
+ "content": {
118
+ "application/json": {
119
+ "schema": {
120
+ "allOf": [
121
+ {
122
+ "$ref": "#/components/schemas/SuccessEnvelope"
123
+ },
124
+ {
125
+ "type": "object",
126
+ "properties": {
127
+ "data": {
128
+ "$ref": "#/components/schemas/AccountUpdated"
129
+ }
130
+ }
131
+ }
132
+ ]
133
+ }
134
+ }
135
+ }
136
+ },
137
+ "400": {
138
+ "$ref": "#/components/responses/ValidationError"
139
+ },
140
+ "401": {
141
+ "$ref": "#/components/responses/Unauthorized"
142
+ },
143
+ "404": {
144
+ "$ref": "#/components/responses/NotFound"
145
+ }
146
+ }
147
+ }
148
+ },
149
+ "/account/storage": {
150
+ "get": {
151
+ "operationId": "getStorageStats",
152
+ "summary": "Get storage usage",
153
+ "tags": [
154
+ "Account"
155
+ ],
156
+ "responses": {
157
+ "200": {
158
+ "description": "Storage statistics",
159
+ "content": {
160
+ "application/json": {
161
+ "schema": {
162
+ "allOf": [
163
+ {
164
+ "$ref": "#/components/schemas/SuccessEnvelope"
165
+ },
166
+ {
167
+ "type": "object",
168
+ "properties": {
169
+ "data": {
170
+ "$ref": "#/components/schemas/StorageStats"
171
+ }
172
+ }
173
+ }
174
+ ]
175
+ }
176
+ }
177
+ }
178
+ },
179
+ "401": {
180
+ "$ref": "#/components/responses/Unauthorized"
181
+ },
182
+ "404": {
183
+ "$ref": "#/components/responses/NotFound"
184
+ }
185
+ }
186
+ }
187
+ },
188
+ "/account/webhook-secret": {
189
+ "get": {
190
+ "operationId": "getWebhookSecret",
191
+ "summary": "Get webhook signing secret",
192
+ "description": "Returns the webhook signing secret for your account. If no secret\nexists yet, one is generated automatically on first access.\n",
193
+ "tags": [
194
+ "Account"
195
+ ],
196
+ "responses": {
197
+ "200": {
198
+ "description": "Webhook secret",
199
+ "content": {
200
+ "application/json": {
201
+ "schema": {
202
+ "allOf": [
203
+ {
204
+ "$ref": "#/components/schemas/SuccessEnvelope"
205
+ },
206
+ {
207
+ "type": "object",
208
+ "properties": {
209
+ "data": {
210
+ "$ref": "#/components/schemas/WebhookSecret"
211
+ }
212
+ }
213
+ }
214
+ ]
215
+ }
216
+ }
217
+ }
218
+ },
219
+ "401": {
220
+ "$ref": "#/components/responses/Unauthorized"
221
+ },
222
+ "404": {
223
+ "$ref": "#/components/responses/NotFound"
224
+ }
225
+ }
226
+ }
227
+ },
228
+ "/account/webhook-secret/rotate": {
229
+ "post": {
230
+ "operationId": "rotateWebhookSecret",
231
+ "summary": "Rotate webhook signing secret",
232
+ "description": "Generates a new webhook signing secret, replacing the current one.\nRate limited to once per 60 minutes.\n",
233
+ "tags": [
234
+ "Account"
235
+ ],
236
+ "responses": {
237
+ "200": {
238
+ "description": "New webhook secret",
239
+ "content": {
240
+ "application/json": {
241
+ "schema": {
242
+ "allOf": [
243
+ {
244
+ "$ref": "#/components/schemas/SuccessEnvelope"
245
+ },
246
+ {
247
+ "type": "object",
248
+ "properties": {
249
+ "data": {
250
+ "$ref": "#/components/schemas/WebhookSecret"
251
+ }
252
+ }
253
+ }
254
+ ]
255
+ }
256
+ }
257
+ }
258
+ },
259
+ "400": {
260
+ "$ref": "#/components/responses/ValidationError"
261
+ },
262
+ "401": {
263
+ "$ref": "#/components/responses/Unauthorized"
264
+ },
265
+ "404": {
266
+ "$ref": "#/components/responses/NotFound"
267
+ },
268
+ "429": {
269
+ "$ref": "#/components/responses/RateLimited"
270
+ }
271
+ }
272
+ }
273
+ },
274
+ "/domains": {
275
+ "get": {
276
+ "operationId": "listDomains",
277
+ "summary": "List all domains",
278
+ "description": "Returns all verified and unverified domains for your organization,\nsorted by creation date (newest first). Each domain includes a\n`verified` boolean to distinguish between the two states.\n",
279
+ "tags": [
280
+ "Domains"
281
+ ],
282
+ "responses": {
283
+ "200": {
284
+ "description": "List of domains",
285
+ "content": {
286
+ "application/json": {
287
+ "schema": {
288
+ "allOf": [
289
+ {
290
+ "$ref": "#/components/schemas/SuccessEnvelope"
291
+ },
292
+ {
293
+ "type": "object",
294
+ "properties": {
295
+ "data": {
296
+ "type": "array",
297
+ "items": {
298
+ "$ref": "#/components/schemas/Domain"
299
+ }
300
+ }
301
+ }
302
+ }
303
+ ]
304
+ }
305
+ }
306
+ }
307
+ },
308
+ "401": {
309
+ "$ref": "#/components/responses/Unauthorized"
310
+ }
311
+ }
312
+ },
313
+ "post": {
314
+ "operationId": "addDomain",
315
+ "summary": "Claim a new domain",
316
+ "description": "Creates an unverified domain claim. You will receive a\n`verification_token` to add as a DNS TXT record before\ncalling the verify endpoint.\n",
317
+ "tags": [
318
+ "Domains"
319
+ ],
320
+ "requestBody": {
321
+ "required": true,
322
+ "content": {
323
+ "application/json": {
324
+ "schema": {
325
+ "$ref": "#/components/schemas/AddDomainInput"
326
+ }
327
+ }
328
+ }
329
+ },
330
+ "responses": {
331
+ "201": {
332
+ "description": "Domain claim created",
333
+ "content": {
334
+ "application/json": {
335
+ "schema": {
336
+ "allOf": [
337
+ {
338
+ "$ref": "#/components/schemas/SuccessEnvelope"
339
+ },
340
+ {
341
+ "type": "object",
342
+ "properties": {
343
+ "data": {
344
+ "$ref": "#/components/schemas/UnverifiedDomain"
345
+ }
346
+ }
347
+ }
348
+ ]
349
+ }
350
+ }
351
+ }
352
+ },
353
+ "400": {
354
+ "$ref": "#/components/responses/ValidationError"
355
+ },
356
+ "401": {
357
+ "$ref": "#/components/responses/Unauthorized"
358
+ }
359
+ }
360
+ }
361
+ },
362
+ "/domains/{id}": {
363
+ "parameters": [
364
+ {
365
+ "$ref": "#/components/parameters/ResourceId"
366
+ }
367
+ ],
368
+ "patch": {
369
+ "operationId": "updateDomain",
370
+ "summary": "Update domain settings",
371
+ "description": "Update a verified domain's settings. Only verified domains can be\nupdated. Per-domain spam thresholds require a Pro plan.\n",
372
+ "tags": [
373
+ "Domains"
374
+ ],
375
+ "requestBody": {
376
+ "required": true,
377
+ "content": {
378
+ "application/json": {
379
+ "schema": {
380
+ "$ref": "#/components/schemas/UpdateDomainInput"
381
+ }
382
+ }
383
+ }
384
+ },
385
+ "responses": {
386
+ "200": {
387
+ "description": "Updated domain",
388
+ "content": {
389
+ "application/json": {
390
+ "schema": {
391
+ "allOf": [
392
+ {
393
+ "$ref": "#/components/schemas/SuccessEnvelope"
394
+ },
395
+ {
396
+ "type": "object",
397
+ "properties": {
398
+ "data": {
399
+ "$ref": "#/components/schemas/VerifiedDomain"
400
+ }
401
+ }
402
+ }
403
+ ]
404
+ }
405
+ }
406
+ }
407
+ },
408
+ "400": {
409
+ "$ref": "#/components/responses/ValidationError"
410
+ },
411
+ "401": {
412
+ "$ref": "#/components/responses/Unauthorized"
413
+ },
414
+ "404": {
415
+ "$ref": "#/components/responses/NotFound"
416
+ }
417
+ }
418
+ },
419
+ "delete": {
420
+ "operationId": "deleteDomain",
421
+ "summary": "Delete a domain",
422
+ "description": "Deletes a verified or unverified domain claim.",
423
+ "tags": [
424
+ "Domains"
425
+ ],
426
+ "responses": {
427
+ "200": {
428
+ "$ref": "#/components/responses/Deleted"
429
+ },
430
+ "400": {
431
+ "$ref": "#/components/responses/ValidationError"
432
+ },
433
+ "401": {
434
+ "$ref": "#/components/responses/Unauthorized"
435
+ },
436
+ "404": {
437
+ "$ref": "#/components/responses/NotFound"
438
+ }
439
+ }
440
+ }
441
+ },
442
+ "/domains/{id}/verify": {
443
+ "parameters": [
444
+ {
445
+ "$ref": "#/components/parameters/ResourceId"
446
+ }
447
+ ],
448
+ "post": {
449
+ "operationId": "verifyDomain",
450
+ "summary": "Verify domain ownership",
451
+ "description": "Checks DNS records (MX and TXT) to verify domain ownership.\nOn success, the domain is promoted from unverified to verified.\nOn failure, returns which checks passed and which failed.\n",
452
+ "tags": [
453
+ "Domains"
454
+ ],
455
+ "responses": {
456
+ "200": {
457
+ "description": "Verification result",
458
+ "content": {
459
+ "application/json": {
460
+ "schema": {
461
+ "allOf": [
462
+ {
463
+ "$ref": "#/components/schemas/SuccessEnvelope"
464
+ },
465
+ {
466
+ "type": "object",
467
+ "properties": {
468
+ "data": {
469
+ "$ref": "#/components/schemas/DomainVerifyResult"
470
+ }
471
+ }
472
+ }
473
+ ]
474
+ }
475
+ }
476
+ }
477
+ },
478
+ "400": {
479
+ "$ref": "#/components/responses/ValidationError"
480
+ },
481
+ "401": {
482
+ "$ref": "#/components/responses/Unauthorized"
483
+ },
484
+ "404": {
485
+ "$ref": "#/components/responses/NotFound"
486
+ }
487
+ }
488
+ }
489
+ },
490
+ "/emails": {
491
+ "get": {
492
+ "operationId": "listEmails",
493
+ "summary": "List emails",
494
+ "description": "Returns a paginated list of received emails. Supports filtering by\ndomain, status, date range, and free-text search across subject,\nsender, and recipient fields.\n",
495
+ "tags": [
496
+ "Emails"
497
+ ],
498
+ "parameters": [
499
+ {
500
+ "$ref": "#/components/parameters/Cursor"
501
+ },
502
+ {
503
+ "$ref": "#/components/parameters/Limit"
504
+ },
505
+ {
506
+ "name": "domain_id",
507
+ "in": "query",
508
+ "schema": {
509
+ "type": "string",
510
+ "format": "uuid"
511
+ },
512
+ "description": "Filter by domain ID"
513
+ },
514
+ {
515
+ "name": "status",
516
+ "in": "query",
517
+ "schema": {
518
+ "type": "string",
519
+ "enum": [
520
+ "pending",
521
+ "accepted",
522
+ "completed",
523
+ "rejected"
524
+ ]
525
+ },
526
+ "description": "Filter by email status"
527
+ },
528
+ {
529
+ "name": "search",
530
+ "in": "query",
531
+ "schema": {
532
+ "type": "string",
533
+ "maxLength": 500
534
+ },
535
+ "description": "Search subject, sender, and recipient (case-insensitive)"
536
+ },
537
+ {
538
+ "name": "date_from",
539
+ "in": "query",
540
+ "schema": {
541
+ "type": "string",
542
+ "format": "date-time"
543
+ },
544
+ "description": "Filter emails created on or after this timestamp"
545
+ },
546
+ {
547
+ "name": "date_to",
548
+ "in": "query",
549
+ "schema": {
550
+ "type": "string",
551
+ "format": "date-time"
552
+ },
553
+ "description": "Filter emails created on or before this timestamp"
554
+ }
555
+ ],
556
+ "responses": {
557
+ "200": {
558
+ "description": "Paginated list of emails",
559
+ "content": {
560
+ "application/json": {
561
+ "schema": {
562
+ "allOf": [
563
+ {
564
+ "$ref": "#/components/schemas/ListEnvelope"
565
+ },
566
+ {
567
+ "type": "object",
568
+ "properties": {
569
+ "data": {
570
+ "type": "array",
571
+ "items": {
572
+ "$ref": "#/components/schemas/EmailSummary"
573
+ }
574
+ }
575
+ }
576
+ }
577
+ ]
578
+ }
579
+ }
580
+ }
581
+ },
582
+ "400": {
583
+ "$ref": "#/components/responses/ValidationError"
584
+ },
585
+ "401": {
586
+ "$ref": "#/components/responses/Unauthorized"
587
+ }
588
+ }
589
+ }
590
+ },
591
+ "/emails/{id}": {
592
+ "parameters": [
593
+ {
594
+ "$ref": "#/components/parameters/ResourceId"
595
+ }
596
+ ],
597
+ "get": {
598
+ "operationId": "getEmail",
599
+ "summary": "Get email details",
600
+ "tags": [
601
+ "Emails"
602
+ ],
603
+ "responses": {
604
+ "200": {
605
+ "description": "Email details",
606
+ "content": {
607
+ "application/json": {
608
+ "schema": {
609
+ "allOf": [
610
+ {
611
+ "$ref": "#/components/schemas/SuccessEnvelope"
612
+ },
613
+ {
614
+ "type": "object",
615
+ "properties": {
616
+ "data": {
617
+ "$ref": "#/components/schemas/EmailDetail"
618
+ }
619
+ }
620
+ }
621
+ ]
622
+ }
623
+ }
624
+ }
625
+ },
626
+ "400": {
627
+ "$ref": "#/components/responses/ValidationError"
628
+ },
629
+ "401": {
630
+ "$ref": "#/components/responses/Unauthorized"
631
+ },
632
+ "404": {
633
+ "$ref": "#/components/responses/NotFound"
634
+ }
635
+ }
636
+ },
637
+ "delete": {
638
+ "operationId": "deleteEmail",
639
+ "summary": "Delete an email",
640
+ "tags": [
641
+ "Emails"
642
+ ],
643
+ "responses": {
644
+ "200": {
645
+ "$ref": "#/components/responses/Deleted"
646
+ },
647
+ "400": {
648
+ "$ref": "#/components/responses/ValidationError"
649
+ },
650
+ "401": {
651
+ "$ref": "#/components/responses/Unauthorized"
652
+ },
653
+ "404": {
654
+ "$ref": "#/components/responses/NotFound"
655
+ }
656
+ }
657
+ }
658
+ },
659
+ "/emails/{id}/raw": {
660
+ "parameters": [
661
+ {
662
+ "$ref": "#/components/parameters/ResourceId"
663
+ }
664
+ ],
665
+ "get": {
666
+ "operationId": "downloadRawEmail",
667
+ "summary": "Download raw email",
668
+ "description": "Downloads the raw RFC 822 email file (.eml). Authenticates via\na signed download token (provided in webhook payloads) or a\nvalid session.\n",
669
+ "tags": [
670
+ "Emails"
671
+ ],
672
+ "security": [
673
+ {
674
+ "BearerAuth": []
675
+ },
676
+ {
677
+ "DownloadToken": []
678
+ }
679
+ ],
680
+ "parameters": [
681
+ {
682
+ "name": "token",
683
+ "in": "query",
684
+ "schema": {
685
+ "type": "string"
686
+ },
687
+ "description": "Signed download token from webhook payload"
688
+ }
689
+ ],
690
+ "responses": {
691
+ "200": {
692
+ "description": "Raw email file",
693
+ "content": {
694
+ "message/rfc822": {
695
+ "schema": {
696
+ "type": "string",
697
+ "format": "binary"
698
+ }
699
+ }
700
+ },
701
+ "headers": {
702
+ "Content-Disposition": {
703
+ "schema": {
704
+ "type": "string",
705
+ "example": "attachment; filename=\"email-id.eml\""
706
+ }
707
+ },
708
+ "X-Content-SHA256": {
709
+ "schema": {
710
+ "type": "string"
711
+ },
712
+ "description": "SHA-256 hex digest of the file"
713
+ }
714
+ }
715
+ },
716
+ "400": {
717
+ "$ref": "#/components/responses/ValidationError"
718
+ },
719
+ "401": {
720
+ "$ref": "#/components/responses/Unauthorized"
721
+ },
722
+ "404": {
723
+ "$ref": "#/components/responses/NotFound"
724
+ }
725
+ }
726
+ }
727
+ },
728
+ "/emails/{id}/attachments.tar.gz": {
729
+ "parameters": [
730
+ {
731
+ "$ref": "#/components/parameters/ResourceId"
732
+ }
733
+ ],
734
+ "get": {
735
+ "operationId": "downloadAttachments",
736
+ "summary": "Download email attachments",
737
+ "description": "Downloads all attachments as a gzip-compressed tar archive.\nAuthenticates via a signed download token (provided in webhook\npayloads) or a valid session.\n",
738
+ "tags": [
739
+ "Emails"
740
+ ],
741
+ "security": [
742
+ {
743
+ "BearerAuth": []
744
+ },
745
+ {
746
+ "DownloadToken": []
747
+ }
748
+ ],
749
+ "parameters": [
750
+ {
751
+ "name": "token",
752
+ "in": "query",
753
+ "schema": {
754
+ "type": "string"
755
+ },
756
+ "description": "Signed download token from webhook payload"
757
+ }
758
+ ],
759
+ "responses": {
760
+ "200": {
761
+ "description": "Attachments archive",
762
+ "content": {
763
+ "application/gzip": {
764
+ "schema": {
765
+ "type": "string",
766
+ "format": "binary"
767
+ }
768
+ }
769
+ },
770
+ "headers": {
771
+ "Content-Disposition": {
772
+ "schema": {
773
+ "type": "string",
774
+ "example": "attachment; filename=\"email-id_attachments.tar.gz\""
775
+ }
776
+ },
777
+ "X-Content-SHA256": {
778
+ "schema": {
779
+ "type": "string"
780
+ },
781
+ "description": "SHA-256 hex digest of the archive"
782
+ },
783
+ "X-Attachment-Count": {
784
+ "schema": {
785
+ "type": "string"
786
+ },
787
+ "description": "Number of attachments in the archive"
788
+ }
789
+ }
790
+ },
791
+ "400": {
792
+ "$ref": "#/components/responses/ValidationError"
793
+ },
794
+ "401": {
795
+ "$ref": "#/components/responses/Unauthorized"
796
+ },
797
+ "404": {
798
+ "$ref": "#/components/responses/NotFound"
799
+ }
800
+ }
801
+ }
802
+ },
803
+ "/emails/{id}/replay": {
804
+ "parameters": [
805
+ {
806
+ "$ref": "#/components/parameters/ResourceId"
807
+ }
808
+ ],
809
+ "post": {
810
+ "operationId": "replayEmailWebhooks",
811
+ "summary": "Replay email webhooks",
812
+ "description": "Re-delivers the webhook payload for this email to all active\nendpoints matching the email's domain. Includes rate limiting\nto prevent stampeding.\n",
813
+ "tags": [
814
+ "Emails"
815
+ ],
816
+ "responses": {
817
+ "200": {
818
+ "description": "Replay result",
819
+ "content": {
820
+ "application/json": {
821
+ "schema": {
822
+ "allOf": [
823
+ {
824
+ "$ref": "#/components/schemas/SuccessEnvelope"
825
+ },
826
+ {
827
+ "type": "object",
828
+ "properties": {
829
+ "data": {
830
+ "$ref": "#/components/schemas/ReplayResult"
831
+ }
832
+ }
833
+ }
834
+ ]
835
+ }
836
+ }
837
+ }
838
+ },
839
+ "400": {
840
+ "$ref": "#/components/responses/ValidationError"
841
+ },
842
+ "401": {
843
+ "$ref": "#/components/responses/Unauthorized"
844
+ },
845
+ "404": {
846
+ "$ref": "#/components/responses/NotFound"
847
+ }
848
+ }
849
+ }
850
+ },
851
+ "/endpoints": {
852
+ "get": {
853
+ "operationId": "listEndpoints",
854
+ "summary": "List webhook endpoints",
855
+ "description": "Returns all active (non-deleted) webhook endpoints.",
856
+ "tags": [
857
+ "Endpoints"
858
+ ],
859
+ "responses": {
860
+ "200": {
861
+ "description": "List of endpoints",
862
+ "content": {
863
+ "application/json": {
864
+ "schema": {
865
+ "allOf": [
866
+ {
867
+ "$ref": "#/components/schemas/SuccessEnvelope"
868
+ },
869
+ {
870
+ "type": "object",
871
+ "properties": {
872
+ "data": {
873
+ "type": "array",
874
+ "items": {
875
+ "$ref": "#/components/schemas/Endpoint"
876
+ }
877
+ }
878
+ }
879
+ }
880
+ ]
881
+ }
882
+ }
883
+ }
884
+ },
885
+ "401": {
886
+ "$ref": "#/components/responses/Unauthorized"
887
+ }
888
+ }
889
+ },
890
+ "post": {
891
+ "operationId": "createEndpoint",
892
+ "summary": "Create a webhook endpoint",
893
+ "description": "Creates a new webhook endpoint. If a deactivated endpoint with the\nsame URL and domain exists, it is reactivated instead.\nSubject to plan limits on the number of active endpoints.\n",
894
+ "tags": [
895
+ "Endpoints"
896
+ ],
897
+ "requestBody": {
898
+ "required": true,
899
+ "content": {
900
+ "application/json": {
901
+ "schema": {
902
+ "$ref": "#/components/schemas/CreateEndpointInput"
903
+ }
904
+ }
905
+ }
906
+ },
907
+ "responses": {
908
+ "201": {
909
+ "description": "Endpoint created (or reactivated)",
910
+ "content": {
911
+ "application/json": {
912
+ "schema": {
913
+ "allOf": [
914
+ {
915
+ "$ref": "#/components/schemas/SuccessEnvelope"
916
+ },
917
+ {
918
+ "type": "object",
919
+ "properties": {
920
+ "data": {
921
+ "$ref": "#/components/schemas/Endpoint"
922
+ }
923
+ }
924
+ }
925
+ ]
926
+ }
927
+ }
928
+ }
929
+ },
930
+ "400": {
931
+ "$ref": "#/components/responses/ValidationError"
932
+ },
933
+ "401": {
934
+ "$ref": "#/components/responses/Unauthorized"
935
+ }
936
+ }
937
+ }
938
+ },
939
+ "/endpoints/{id}": {
940
+ "parameters": [
941
+ {
942
+ "$ref": "#/components/parameters/ResourceId"
943
+ }
944
+ ],
945
+ "patch": {
946
+ "operationId": "updateEndpoint",
947
+ "summary": "Update a webhook endpoint",
948
+ "description": "Updates an active webhook endpoint. If the URL is changed, the old\nendpoint is deactivated and a new one is created (or an existing\ndeactivated endpoint with the new URL is reactivated).\n",
949
+ "tags": [
950
+ "Endpoints"
951
+ ],
952
+ "requestBody": {
953
+ "required": true,
954
+ "content": {
955
+ "application/json": {
956
+ "schema": {
957
+ "$ref": "#/components/schemas/UpdateEndpointInput"
958
+ }
959
+ }
960
+ }
961
+ },
962
+ "responses": {
963
+ "200": {
964
+ "description": "Updated endpoint",
965
+ "content": {
966
+ "application/json": {
967
+ "schema": {
968
+ "allOf": [
969
+ {
970
+ "$ref": "#/components/schemas/SuccessEnvelope"
971
+ },
972
+ {
973
+ "type": "object",
974
+ "properties": {
975
+ "data": {
976
+ "$ref": "#/components/schemas/Endpoint"
977
+ }
978
+ }
979
+ }
980
+ ]
981
+ }
982
+ }
983
+ }
984
+ },
985
+ "400": {
986
+ "$ref": "#/components/responses/ValidationError"
987
+ },
988
+ "401": {
989
+ "$ref": "#/components/responses/Unauthorized"
990
+ },
991
+ "404": {
992
+ "$ref": "#/components/responses/NotFound"
993
+ }
994
+ }
995
+ },
996
+ "delete": {
997
+ "operationId": "deleteEndpoint",
998
+ "summary": "Delete a webhook endpoint",
999
+ "description": "Soft-deletes a webhook endpoint. The endpoint will no longer\nreceive webhook deliveries.\n",
1000
+ "tags": [
1001
+ "Endpoints"
1002
+ ],
1003
+ "responses": {
1004
+ "200": {
1005
+ "$ref": "#/components/responses/Deleted"
1006
+ },
1007
+ "400": {
1008
+ "$ref": "#/components/responses/ValidationError"
1009
+ },
1010
+ "401": {
1011
+ "$ref": "#/components/responses/Unauthorized"
1012
+ },
1013
+ "404": {
1014
+ "$ref": "#/components/responses/NotFound"
1015
+ }
1016
+ }
1017
+ }
1018
+ },
1019
+ "/endpoints/{id}/test": {
1020
+ "parameters": [
1021
+ {
1022
+ "$ref": "#/components/parameters/ResourceId"
1023
+ }
1024
+ ],
1025
+ "post": {
1026
+ "operationId": "testEndpoint",
1027
+ "summary": "Send a test webhook",
1028
+ "description": "Sends a sample `email.received` event to the endpoint. The request\nincludes SSRF protection (private IP rejection and DNS pinning).\nRate limited to 4 per minute and 30 per hour (non-exempt).\nSuccessful deliveries and verified-domain endpoints are exempt\nfrom the rate limit.\n",
1029
+ "tags": [
1030
+ "Endpoints"
1031
+ ],
1032
+ "responses": {
1033
+ "200": {
1034
+ "description": "Test result",
1035
+ "content": {
1036
+ "application/json": {
1037
+ "schema": {
1038
+ "allOf": [
1039
+ {
1040
+ "$ref": "#/components/schemas/SuccessEnvelope"
1041
+ },
1042
+ {
1043
+ "type": "object",
1044
+ "properties": {
1045
+ "data": {
1046
+ "$ref": "#/components/schemas/TestResult"
1047
+ }
1048
+ }
1049
+ }
1050
+ ]
1051
+ }
1052
+ }
1053
+ }
1054
+ },
1055
+ "400": {
1056
+ "$ref": "#/components/responses/ValidationError"
1057
+ },
1058
+ "401": {
1059
+ "$ref": "#/components/responses/Unauthorized"
1060
+ },
1061
+ "404": {
1062
+ "$ref": "#/components/responses/NotFound"
1063
+ },
1064
+ "429": {
1065
+ "$ref": "#/components/responses/RateLimited"
1066
+ }
1067
+ }
1068
+ }
1069
+ },
1070
+ "/filters": {
1071
+ "get": {
1072
+ "operationId": "listFilters",
1073
+ "summary": "List filter rules",
1074
+ "description": "Returns all whitelist and blocklist filter rules.",
1075
+ "tags": [
1076
+ "Filters"
1077
+ ],
1078
+ "responses": {
1079
+ "200": {
1080
+ "description": "List of filters",
1081
+ "content": {
1082
+ "application/json": {
1083
+ "schema": {
1084
+ "allOf": [
1085
+ {
1086
+ "$ref": "#/components/schemas/SuccessEnvelope"
1087
+ },
1088
+ {
1089
+ "type": "object",
1090
+ "properties": {
1091
+ "data": {
1092
+ "type": "array",
1093
+ "items": {
1094
+ "$ref": "#/components/schemas/Filter"
1095
+ }
1096
+ }
1097
+ }
1098
+ }
1099
+ ]
1100
+ }
1101
+ }
1102
+ }
1103
+ },
1104
+ "401": {
1105
+ "$ref": "#/components/responses/Unauthorized"
1106
+ }
1107
+ }
1108
+ },
1109
+ "post": {
1110
+ "operationId": "createFilter",
1111
+ "summary": "Create a filter rule",
1112
+ "description": "Creates a new whitelist or blocklist filter. Per-domain filters\nrequire a Pro plan. Patterns are stored as lowercase.\n",
1113
+ "tags": [
1114
+ "Filters"
1115
+ ],
1116
+ "requestBody": {
1117
+ "required": true,
1118
+ "content": {
1119
+ "application/json": {
1120
+ "schema": {
1121
+ "$ref": "#/components/schemas/CreateFilterInput"
1122
+ }
1123
+ }
1124
+ }
1125
+ },
1126
+ "responses": {
1127
+ "201": {
1128
+ "description": "Filter created",
1129
+ "content": {
1130
+ "application/json": {
1131
+ "schema": {
1132
+ "allOf": [
1133
+ {
1134
+ "$ref": "#/components/schemas/SuccessEnvelope"
1135
+ },
1136
+ {
1137
+ "type": "object",
1138
+ "properties": {
1139
+ "data": {
1140
+ "$ref": "#/components/schemas/Filter"
1141
+ }
1142
+ }
1143
+ }
1144
+ ]
1145
+ }
1146
+ }
1147
+ }
1148
+ },
1149
+ "400": {
1150
+ "$ref": "#/components/responses/ValidationError"
1151
+ },
1152
+ "401": {
1153
+ "$ref": "#/components/responses/Unauthorized"
1154
+ },
1155
+ "404": {
1156
+ "$ref": "#/components/responses/NotFound"
1157
+ }
1158
+ }
1159
+ }
1160
+ },
1161
+ "/filters/{id}": {
1162
+ "parameters": [
1163
+ {
1164
+ "$ref": "#/components/parameters/ResourceId"
1165
+ }
1166
+ ],
1167
+ "patch": {
1168
+ "operationId": "updateFilter",
1169
+ "summary": "Update a filter rule",
1170
+ "description": "Toggle a filter's enabled state.",
1171
+ "tags": [
1172
+ "Filters"
1173
+ ],
1174
+ "requestBody": {
1175
+ "required": true,
1176
+ "content": {
1177
+ "application/json": {
1178
+ "schema": {
1179
+ "$ref": "#/components/schemas/UpdateFilterInput"
1180
+ }
1181
+ }
1182
+ }
1183
+ },
1184
+ "responses": {
1185
+ "200": {
1186
+ "description": "Updated filter",
1187
+ "content": {
1188
+ "application/json": {
1189
+ "schema": {
1190
+ "allOf": [
1191
+ {
1192
+ "$ref": "#/components/schemas/SuccessEnvelope"
1193
+ },
1194
+ {
1195
+ "type": "object",
1196
+ "properties": {
1197
+ "data": {
1198
+ "$ref": "#/components/schemas/Filter"
1199
+ }
1200
+ }
1201
+ }
1202
+ ]
1203
+ }
1204
+ }
1205
+ }
1206
+ },
1207
+ "400": {
1208
+ "$ref": "#/components/responses/ValidationError"
1209
+ },
1210
+ "401": {
1211
+ "$ref": "#/components/responses/Unauthorized"
1212
+ },
1213
+ "404": {
1214
+ "$ref": "#/components/responses/NotFound"
1215
+ }
1216
+ }
1217
+ },
1218
+ "delete": {
1219
+ "operationId": "deleteFilter",
1220
+ "summary": "Delete a filter rule",
1221
+ "tags": [
1222
+ "Filters"
1223
+ ],
1224
+ "responses": {
1225
+ "200": {
1226
+ "$ref": "#/components/responses/Deleted"
1227
+ },
1228
+ "400": {
1229
+ "$ref": "#/components/responses/ValidationError"
1230
+ },
1231
+ "401": {
1232
+ "$ref": "#/components/responses/Unauthorized"
1233
+ },
1234
+ "404": {
1235
+ "$ref": "#/components/responses/NotFound"
1236
+ }
1237
+ }
1238
+ }
1239
+ },
1240
+ "/webhooks/deliveries": {
1241
+ "get": {
1242
+ "operationId": "listDeliveries",
1243
+ "summary": "List webhook deliveries",
1244
+ "description": "Returns a paginated list of webhook delivery attempts. Each delivery\nincludes a nested `email` object with sender, recipient, and subject.\n",
1245
+ "tags": [
1246
+ "Webhook Deliveries"
1247
+ ],
1248
+ "parameters": [
1249
+ {
1250
+ "$ref": "#/components/parameters/Cursor"
1251
+ },
1252
+ {
1253
+ "$ref": "#/components/parameters/Limit"
1254
+ },
1255
+ {
1256
+ "name": "email_id",
1257
+ "in": "query",
1258
+ "schema": {
1259
+ "type": "string",
1260
+ "format": "uuid"
1261
+ },
1262
+ "description": "Filter by email ID"
1263
+ },
1264
+ {
1265
+ "name": "status",
1266
+ "in": "query",
1267
+ "schema": {
1268
+ "type": "string",
1269
+ "enum": [
1270
+ "pending",
1271
+ "delivered",
1272
+ "header_confirmed",
1273
+ "failed"
1274
+ ]
1275
+ },
1276
+ "description": "Filter by delivery status"
1277
+ },
1278
+ {
1279
+ "name": "date_from",
1280
+ "in": "query",
1281
+ "schema": {
1282
+ "type": "string",
1283
+ "format": "date-time"
1284
+ },
1285
+ "description": "Filter deliveries created on or after this timestamp"
1286
+ },
1287
+ {
1288
+ "name": "date_to",
1289
+ "in": "query",
1290
+ "schema": {
1291
+ "type": "string",
1292
+ "format": "date-time"
1293
+ },
1294
+ "description": "Filter deliveries created on or before this timestamp"
1295
+ }
1296
+ ],
1297
+ "responses": {
1298
+ "200": {
1299
+ "description": "Paginated list of deliveries",
1300
+ "content": {
1301
+ "application/json": {
1302
+ "schema": {
1303
+ "allOf": [
1304
+ {
1305
+ "$ref": "#/components/schemas/ListEnvelope"
1306
+ },
1307
+ {
1308
+ "type": "object",
1309
+ "properties": {
1310
+ "data": {
1311
+ "type": "array",
1312
+ "items": {
1313
+ "$ref": "#/components/schemas/DeliverySummary"
1314
+ }
1315
+ }
1316
+ }
1317
+ }
1318
+ ]
1319
+ }
1320
+ }
1321
+ }
1322
+ },
1323
+ "400": {
1324
+ "$ref": "#/components/responses/ValidationError"
1325
+ },
1326
+ "401": {
1327
+ "$ref": "#/components/responses/Unauthorized"
1328
+ }
1329
+ }
1330
+ }
1331
+ },
1332
+ "/webhooks/deliveries/{id}/replay": {
1333
+ "parameters": [
1334
+ {
1335
+ "name": "id",
1336
+ "in": "path",
1337
+ "required": true,
1338
+ "schema": {
1339
+ "type": "string",
1340
+ "pattern": "^\\d+$"
1341
+ },
1342
+ "description": "Delivery ID (numeric)"
1343
+ }
1344
+ ],
1345
+ "post": {
1346
+ "operationId": "replayDelivery",
1347
+ "summary": "Replay a webhook delivery",
1348
+ "description": "Re-sends the stored webhook payload from a previous delivery attempt.\nIf the original endpoint is still active, it is targeted. If the\noriginal endpoint was deleted, the first active endpoint is used.\nDeactivated endpoints cannot be replayed to.\n",
1349
+ "tags": [
1350
+ "Webhook Deliveries"
1351
+ ],
1352
+ "responses": {
1353
+ "200": {
1354
+ "description": "Replay result",
1355
+ "content": {
1356
+ "application/json": {
1357
+ "schema": {
1358
+ "allOf": [
1359
+ {
1360
+ "$ref": "#/components/schemas/SuccessEnvelope"
1361
+ },
1362
+ {
1363
+ "type": "object",
1364
+ "properties": {
1365
+ "data": {
1366
+ "$ref": "#/components/schemas/ReplayResult"
1367
+ }
1368
+ }
1369
+ }
1370
+ ]
1371
+ }
1372
+ }
1373
+ }
1374
+ },
1375
+ "400": {
1376
+ "$ref": "#/components/responses/ValidationError"
1377
+ },
1378
+ "401": {
1379
+ "$ref": "#/components/responses/Unauthorized"
1380
+ },
1381
+ "404": {
1382
+ "$ref": "#/components/responses/NotFound"
1383
+ }
1384
+ }
1385
+ }
1386
+ }
1387
+ },
1388
+ "components": {
1389
+ "securitySchemes": {
1390
+ "BearerAuth": {
1391
+ "type": "http",
1392
+ "scheme": "bearer",
1393
+ "description": "API key with `prim_` prefix: `Authorization: Bearer prim_<key>`"
1394
+ },
1395
+ "DownloadToken": {
1396
+ "type": "apiKey",
1397
+ "in": "query",
1398
+ "name": "token",
1399
+ "description": "Signed download token provided in webhook payloads"
1400
+ }
1401
+ },
1402
+ "parameters": {
1403
+ "ResourceId": {
1404
+ "name": "id",
1405
+ "in": "path",
1406
+ "required": true,
1407
+ "schema": {
1408
+ "type": "string",
1409
+ "format": "uuid"
1410
+ },
1411
+ "description": "Resource UUID"
1412
+ },
1413
+ "Cursor": {
1414
+ "name": "cursor",
1415
+ "in": "query",
1416
+ "schema": {
1417
+ "type": "string"
1418
+ },
1419
+ "description": "Pagination cursor from a previous response's `meta.cursor` field.\nFormat: `{ISO-datetime}|{id}`\n"
1420
+ },
1421
+ "Limit": {
1422
+ "name": "limit",
1423
+ "in": "query",
1424
+ "schema": {
1425
+ "type": "integer",
1426
+ "minimum": 1,
1427
+ "maximum": 100,
1428
+ "default": 50
1429
+ },
1430
+ "description": "Number of results per page"
1431
+ }
1432
+ },
1433
+ "responses": {
1434
+ "Unauthorized": {
1435
+ "description": "Invalid or missing API key",
1436
+ "content": {
1437
+ "application/json": {
1438
+ "schema": {
1439
+ "$ref": "#/components/schemas/ErrorResponse"
1440
+ },
1441
+ "example": {
1442
+ "success": false,
1443
+ "error": {
1444
+ "code": "unauthorized",
1445
+ "message": "Invalid or missing API key"
1446
+ }
1447
+ }
1448
+ }
1449
+ }
1450
+ },
1451
+ "NotFound": {
1452
+ "description": "Resource not found",
1453
+ "content": {
1454
+ "application/json": {
1455
+ "schema": {
1456
+ "$ref": "#/components/schemas/ErrorResponse"
1457
+ },
1458
+ "example": {
1459
+ "success": false,
1460
+ "error": {
1461
+ "code": "not_found",
1462
+ "message": "Resource not found"
1463
+ }
1464
+ }
1465
+ }
1466
+ }
1467
+ },
1468
+ "ValidationError": {
1469
+ "description": "Invalid request parameters",
1470
+ "content": {
1471
+ "application/json": {
1472
+ "schema": {
1473
+ "$ref": "#/components/schemas/ErrorResponse"
1474
+ },
1475
+ "example": {
1476
+ "success": false,
1477
+ "error": {
1478
+ "code": "validation_error",
1479
+ "message": "Invalid domain format"
1480
+ }
1481
+ }
1482
+ }
1483
+ }
1484
+ },
1485
+ "RateLimited": {
1486
+ "description": "Rate limit exceeded",
1487
+ "headers": {
1488
+ "Retry-After": {
1489
+ "schema": {
1490
+ "type": "integer"
1491
+ },
1492
+ "description": "Seconds to wait before retrying"
1493
+ }
1494
+ },
1495
+ "content": {
1496
+ "application/json": {
1497
+ "schema": {
1498
+ "$ref": "#/components/schemas/ErrorResponse"
1499
+ },
1500
+ "example": {
1501
+ "success": false,
1502
+ "error": {
1503
+ "code": "rate_limit_exceeded",
1504
+ "message": "Rate limit exceeded"
1505
+ }
1506
+ }
1507
+ }
1508
+ }
1509
+ },
1510
+ "Deleted": {
1511
+ "description": "Resource deleted",
1512
+ "content": {
1513
+ "application/json": {
1514
+ "schema": {
1515
+ "allOf": [
1516
+ {
1517
+ "$ref": "#/components/schemas/SuccessEnvelope"
1518
+ },
1519
+ {
1520
+ "type": "object",
1521
+ "properties": {
1522
+ "data": {
1523
+ "type": "object",
1524
+ "properties": {
1525
+ "deleted": {
1526
+ "type": "boolean",
1527
+ "const": true
1528
+ }
1529
+ },
1530
+ "required": [
1531
+ "deleted"
1532
+ ]
1533
+ }
1534
+ }
1535
+ }
1536
+ ]
1537
+ }
1538
+ }
1539
+ }
1540
+ }
1541
+ },
1542
+ "schemas": {
1543
+ "SuccessEnvelope": {
1544
+ "type": "object",
1545
+ "properties": {
1546
+ "success": {
1547
+ "type": "boolean",
1548
+ "const": true
1549
+ }
1550
+ },
1551
+ "required": [
1552
+ "success",
1553
+ "data"
1554
+ ]
1555
+ },
1556
+ "ListEnvelope": {
1557
+ "type": "object",
1558
+ "properties": {
1559
+ "success": {
1560
+ "type": "boolean",
1561
+ "const": true
1562
+ },
1563
+ "meta": {
1564
+ "$ref": "#/components/schemas/PaginationMeta"
1565
+ }
1566
+ },
1567
+ "required": [
1568
+ "success",
1569
+ "data",
1570
+ "meta"
1571
+ ]
1572
+ },
1573
+ "PaginationMeta": {
1574
+ "type": "object",
1575
+ "properties": {
1576
+ "total": {
1577
+ "type": "integer",
1578
+ "description": "Total number of matching records"
1579
+ },
1580
+ "limit": {
1581
+ "type": "integer",
1582
+ "description": "Page size used for this request"
1583
+ },
1584
+ "cursor": {
1585
+ "type": [
1586
+ "string",
1587
+ "null"
1588
+ ],
1589
+ "description": "Cursor for the next page, or null if no more results"
1590
+ }
1591
+ },
1592
+ "required": [
1593
+ "total",
1594
+ "limit",
1595
+ "cursor"
1596
+ ]
1597
+ },
1598
+ "ErrorResponse": {
1599
+ "type": "object",
1600
+ "properties": {
1601
+ "success": {
1602
+ "type": "boolean",
1603
+ "const": false
1604
+ },
1605
+ "error": {
1606
+ "type": "object",
1607
+ "properties": {
1608
+ "code": {
1609
+ "type": "string",
1610
+ "enum": [
1611
+ "unauthorized",
1612
+ "forbidden",
1613
+ "not_found",
1614
+ "validation_error",
1615
+ "rate_limit_exceeded",
1616
+ "internal_error"
1617
+ ]
1618
+ },
1619
+ "message": {
1620
+ "type": "string"
1621
+ }
1622
+ },
1623
+ "required": [
1624
+ "code",
1625
+ "message"
1626
+ ]
1627
+ }
1628
+ },
1629
+ "required": [
1630
+ "success",
1631
+ "error"
1632
+ ]
1633
+ },
1634
+ "Account": {
1635
+ "type": "object",
1636
+ "properties": {
1637
+ "id": {
1638
+ "type": "string",
1639
+ "format": "uuid"
1640
+ },
1641
+ "email": {
1642
+ "type": "string"
1643
+ },
1644
+ "plan": {
1645
+ "type": "string"
1646
+ },
1647
+ "created_at": {
1648
+ "type": "string",
1649
+ "format": "date-time"
1650
+ },
1651
+ "onboarding_completed": {
1652
+ "type": "boolean"
1653
+ },
1654
+ "onboarding_step": {
1655
+ "type": [
1656
+ "string",
1657
+ "null"
1658
+ ]
1659
+ },
1660
+ "stripe_subscription_status": {
1661
+ "type": [
1662
+ "string",
1663
+ "null"
1664
+ ]
1665
+ },
1666
+ "subscription_current_period_end": {
1667
+ "type": [
1668
+ "string",
1669
+ "null"
1670
+ ],
1671
+ "format": "date-time"
1672
+ },
1673
+ "subscription_cancel_at_period_end": {
1674
+ "type": [
1675
+ "boolean",
1676
+ "null"
1677
+ ]
1678
+ },
1679
+ "spam_threshold": {
1680
+ "type": [
1681
+ "number",
1682
+ "null"
1683
+ ],
1684
+ "minimum": 0,
1685
+ "maximum": 15
1686
+ },
1687
+ "discard_content_on_webhook_confirmed": {
1688
+ "type": "boolean"
1689
+ },
1690
+ "webhook_secret_rotated_at": {
1691
+ "type": [
1692
+ "string",
1693
+ "null"
1694
+ ],
1695
+ "format": "date-time"
1696
+ }
1697
+ },
1698
+ "required": [
1699
+ "id",
1700
+ "email",
1701
+ "plan",
1702
+ "created_at",
1703
+ "discard_content_on_webhook_confirmed"
1704
+ ]
1705
+ },
1706
+ "AccountUpdated": {
1707
+ "type": "object",
1708
+ "properties": {
1709
+ "id": {
1710
+ "type": "string",
1711
+ "format": "uuid"
1712
+ },
1713
+ "email": {
1714
+ "type": "string"
1715
+ },
1716
+ "plan": {
1717
+ "type": "string"
1718
+ },
1719
+ "spam_threshold": {
1720
+ "type": [
1721
+ "number",
1722
+ "null"
1723
+ ],
1724
+ "minimum": 0,
1725
+ "maximum": 15
1726
+ },
1727
+ "discard_content_on_webhook_confirmed": {
1728
+ "type": "boolean"
1729
+ }
1730
+ },
1731
+ "required": [
1732
+ "id",
1733
+ "email",
1734
+ "plan",
1735
+ "discard_content_on_webhook_confirmed"
1736
+ ]
1737
+ },
1738
+ "UpdateAccountInput": {
1739
+ "type": "object",
1740
+ "additionalProperties": false,
1741
+ "properties": {
1742
+ "spam_threshold": {
1743
+ "type": [
1744
+ "number",
1745
+ "null"
1746
+ ],
1747
+ "minimum": 0,
1748
+ "maximum": 15,
1749
+ "description": "Global spam score threshold (0-15). Emails scoring above this are rejected. Set to null to disable."
1750
+ },
1751
+ "discard_content_on_webhook_confirmed": {
1752
+ "type": "boolean",
1753
+ "description": "Whether to discard email content after the webhook endpoint confirms receipt."
1754
+ }
1755
+ },
1756
+ "minProperties": 1
1757
+ },
1758
+ "StorageStats": {
1759
+ "type": "object",
1760
+ "properties": {
1761
+ "used_bytes": {
1762
+ "type": "integer",
1763
+ "description": "Total storage used in bytes"
1764
+ },
1765
+ "used_kb": {
1766
+ "type": "number",
1767
+ "description": "Total storage used in kilobytes (1 decimal)"
1768
+ },
1769
+ "used_mb": {
1770
+ "type": "number",
1771
+ "description": "Total storage used in megabytes (2 decimals)"
1772
+ },
1773
+ "quota_mb": {
1774
+ "type": "number",
1775
+ "description": "Storage quota in megabytes (based on plan)"
1776
+ },
1777
+ "percentage": {
1778
+ "type": "number",
1779
+ "description": "Percentage of quota used (1 decimal)"
1780
+ },
1781
+ "emails_count": {
1782
+ "type": "integer",
1783
+ "description": "Number of stored emails"
1784
+ }
1785
+ },
1786
+ "required": [
1787
+ "used_bytes",
1788
+ "used_kb",
1789
+ "used_mb",
1790
+ "quota_mb",
1791
+ "percentage",
1792
+ "emails_count"
1793
+ ]
1794
+ },
1795
+ "WebhookSecret": {
1796
+ "type": "object",
1797
+ "properties": {
1798
+ "secret": {
1799
+ "type": "string",
1800
+ "description": "The webhook signing secret value"
1801
+ }
1802
+ },
1803
+ "required": [
1804
+ "secret"
1805
+ ]
1806
+ },
1807
+ "Domain": {
1808
+ "description": "A domain can be either verified or unverified. Verified domains have\n`is_active` and `spam_threshold` fields. Unverified domains have a\n`verification_token` for DNS verification.\n",
1809
+ "oneOf": [
1810
+ {
1811
+ "$ref": "#/components/schemas/VerifiedDomain"
1812
+ },
1813
+ {
1814
+ "$ref": "#/components/schemas/UnverifiedDomain"
1815
+ }
1816
+ ]
1817
+ },
1818
+ "VerifiedDomain": {
1819
+ "type": "object",
1820
+ "properties": {
1821
+ "id": {
1822
+ "type": "string",
1823
+ "format": "uuid"
1824
+ },
1825
+ "org_id": {
1826
+ "type": "string",
1827
+ "format": "uuid"
1828
+ },
1829
+ "domain": {
1830
+ "type": "string"
1831
+ },
1832
+ "verified": {
1833
+ "type": "boolean",
1834
+ "const": true
1835
+ },
1836
+ "is_active": {
1837
+ "type": "boolean"
1838
+ },
1839
+ "spam_threshold": {
1840
+ "type": [
1841
+ "number",
1842
+ "null"
1843
+ ],
1844
+ "minimum": 0,
1845
+ "maximum": 15
1846
+ },
1847
+ "verification_token": {
1848
+ "type": [
1849
+ "string",
1850
+ "null"
1851
+ ]
1852
+ },
1853
+ "created_at": {
1854
+ "type": "string",
1855
+ "format": "date-time"
1856
+ }
1857
+ },
1858
+ "required": [
1859
+ "id",
1860
+ "org_id",
1861
+ "domain",
1862
+ "verified",
1863
+ "is_active",
1864
+ "created_at"
1865
+ ]
1866
+ },
1867
+ "UnverifiedDomain": {
1868
+ "type": "object",
1869
+ "properties": {
1870
+ "id": {
1871
+ "type": "string",
1872
+ "format": "uuid"
1873
+ },
1874
+ "org_id": {
1875
+ "type": "string",
1876
+ "format": "uuid"
1877
+ },
1878
+ "domain": {
1879
+ "type": "string"
1880
+ },
1881
+ "verified": {
1882
+ "type": "boolean",
1883
+ "const": false
1884
+ },
1885
+ "verification_token": {
1886
+ "type": "string",
1887
+ "description": "Add this value as a TXT record to verify ownership"
1888
+ },
1889
+ "created_at": {
1890
+ "type": "string",
1891
+ "format": "date-time"
1892
+ }
1893
+ },
1894
+ "required": [
1895
+ "id",
1896
+ "org_id",
1897
+ "domain",
1898
+ "verified",
1899
+ "verification_token",
1900
+ "created_at"
1901
+ ]
1902
+ },
1903
+ "AddDomainInput": {
1904
+ "type": "object",
1905
+ "additionalProperties": false,
1906
+ "properties": {
1907
+ "domain": {
1908
+ "type": "string",
1909
+ "minLength": 1,
1910
+ "maxLength": 253,
1911
+ "description": "The domain name to claim (e.g. \"example.com\")"
1912
+ }
1913
+ },
1914
+ "required": [
1915
+ "domain"
1916
+ ]
1917
+ },
1918
+ "UpdateDomainInput": {
1919
+ "type": "object",
1920
+ "additionalProperties": false,
1921
+ "properties": {
1922
+ "is_active": {
1923
+ "type": "boolean",
1924
+ "description": "Whether the domain accepts incoming emails"
1925
+ },
1926
+ "spam_threshold": {
1927
+ "type": [
1928
+ "number",
1929
+ "null"
1930
+ ],
1931
+ "minimum": 0,
1932
+ "maximum": 15,
1933
+ "description": "Per-domain spam threshold override (Pro plan required)"
1934
+ }
1935
+ },
1936
+ "minProperties": 1
1937
+ },
1938
+ "DomainVerifyResult": {
1939
+ "oneOf": [
1940
+ {
1941
+ "type": "object",
1942
+ "properties": {
1943
+ "verified": {
1944
+ "type": "boolean",
1945
+ "const": true
1946
+ }
1947
+ },
1948
+ "required": [
1949
+ "verified"
1950
+ ]
1951
+ },
1952
+ {
1953
+ "type": "object",
1954
+ "properties": {
1955
+ "verified": {
1956
+ "type": "boolean",
1957
+ "const": false
1958
+ },
1959
+ "mxFound": {
1960
+ "type": "boolean",
1961
+ "description": "Whether MX records point to Primitive"
1962
+ },
1963
+ "txtFound": {
1964
+ "type": "boolean",
1965
+ "description": "Whether the TXT verification record was found"
1966
+ },
1967
+ "error": {
1968
+ "type": "string",
1969
+ "description": "Human-readable verification failure reason"
1970
+ }
1971
+ },
1972
+ "required": [
1973
+ "verified",
1974
+ "mxFound",
1975
+ "txtFound",
1976
+ "error"
1977
+ ]
1978
+ }
1979
+ ]
1980
+ },
1981
+ "EmailSummary": {
1982
+ "type": "object",
1983
+ "properties": {
1984
+ "id": {
1985
+ "type": "string",
1986
+ "format": "uuid"
1987
+ },
1988
+ "message_id": {
1989
+ "type": [
1990
+ "string",
1991
+ "null"
1992
+ ]
1993
+ },
1994
+ "domain_id": {
1995
+ "type": [
1996
+ "string",
1997
+ "null"
1998
+ ],
1999
+ "format": "uuid"
2000
+ },
2001
+ "org_id": {
2002
+ "type": [
2003
+ "string",
2004
+ "null"
2005
+ ],
2006
+ "format": "uuid"
2007
+ },
2008
+ "status": {
2009
+ "type": "string",
2010
+ "enum": [
2011
+ "pending",
2012
+ "accepted",
2013
+ "completed",
2014
+ "rejected"
2015
+ ]
2016
+ },
2017
+ "sender": {
2018
+ "type": "string"
2019
+ },
2020
+ "recipient": {
2021
+ "type": "string"
2022
+ },
2023
+ "subject": {
2024
+ "type": [
2025
+ "string",
2026
+ "null"
2027
+ ]
2028
+ },
2029
+ "domain": {
2030
+ "type": "string"
2031
+ },
2032
+ "spam_score": {
2033
+ "type": [
2034
+ "number",
2035
+ "null"
2036
+ ]
2037
+ },
2038
+ "created_at": {
2039
+ "type": "string",
2040
+ "format": "date-time"
2041
+ },
2042
+ "received_at": {
2043
+ "type": "string",
2044
+ "format": "date-time"
2045
+ },
2046
+ "raw_size_bytes": {
2047
+ "type": [
2048
+ "integer",
2049
+ "null"
2050
+ ]
2051
+ },
2052
+ "webhook_status": {
2053
+ "type": [
2054
+ "string",
2055
+ "null"
2056
+ ],
2057
+ "enum": [
2058
+ "pending",
2059
+ "in_flight",
2060
+ "fired",
2061
+ "failed",
2062
+ "exhausted",
2063
+ null
2064
+ ]
2065
+ },
2066
+ "webhook_attempt_count": {
2067
+ "type": "integer"
2068
+ }
2069
+ },
2070
+ "required": [
2071
+ "id",
2072
+ "status",
2073
+ "sender",
2074
+ "recipient",
2075
+ "domain",
2076
+ "created_at",
2077
+ "received_at",
2078
+ "webhook_attempt_count"
2079
+ ]
2080
+ },
2081
+ "EmailDetail": {
2082
+ "type": "object",
2083
+ "properties": {
2084
+ "id": {
2085
+ "type": "string",
2086
+ "format": "uuid"
2087
+ },
2088
+ "message_id": {
2089
+ "type": [
2090
+ "string",
2091
+ "null"
2092
+ ]
2093
+ },
2094
+ "domain_id": {
2095
+ "type": [
2096
+ "string",
2097
+ "null"
2098
+ ],
2099
+ "format": "uuid"
2100
+ },
2101
+ "org_id": {
2102
+ "type": [
2103
+ "string",
2104
+ "null"
2105
+ ],
2106
+ "format": "uuid"
2107
+ },
2108
+ "sender": {
2109
+ "type": "string"
2110
+ },
2111
+ "recipient": {
2112
+ "type": "string"
2113
+ },
2114
+ "subject": {
2115
+ "type": [
2116
+ "string",
2117
+ "null"
2118
+ ]
2119
+ },
2120
+ "status": {
2121
+ "type": "string",
2122
+ "enum": [
2123
+ "pending",
2124
+ "accepted",
2125
+ "completed",
2126
+ "rejected"
2127
+ ]
2128
+ },
2129
+ "domain": {
2130
+ "type": "string"
2131
+ },
2132
+ "spam_score": {
2133
+ "type": [
2134
+ "number",
2135
+ "null"
2136
+ ]
2137
+ },
2138
+ "raw_size_bytes": {
2139
+ "type": [
2140
+ "integer",
2141
+ "null"
2142
+ ]
2143
+ },
2144
+ "raw_sha256": {
2145
+ "type": [
2146
+ "string",
2147
+ "null"
2148
+ ]
2149
+ },
2150
+ "created_at": {
2151
+ "type": "string",
2152
+ "format": "date-time"
2153
+ },
2154
+ "received_at": {
2155
+ "type": "string",
2156
+ "format": "date-time"
2157
+ },
2158
+ "rejection_reason": {
2159
+ "type": [
2160
+ "string",
2161
+ "null"
2162
+ ]
2163
+ },
2164
+ "webhook_status": {
2165
+ "type": [
2166
+ "string",
2167
+ "null"
2168
+ ],
2169
+ "enum": [
2170
+ "pending",
2171
+ "in_flight",
2172
+ "fired",
2173
+ "failed",
2174
+ "exhausted",
2175
+ null
2176
+ ]
2177
+ },
2178
+ "webhook_attempt_count": {
2179
+ "type": "integer"
2180
+ },
2181
+ "webhook_last_attempt_at": {
2182
+ "type": [
2183
+ "string",
2184
+ "null"
2185
+ ],
2186
+ "format": "date-time"
2187
+ },
2188
+ "webhook_last_status_code": {
2189
+ "type": [
2190
+ "integer",
2191
+ "null"
2192
+ ]
2193
+ },
2194
+ "webhook_last_error": {
2195
+ "type": [
2196
+ "string",
2197
+ "null"
2198
+ ]
2199
+ },
2200
+ "webhook_fired_at": {
2201
+ "type": [
2202
+ "string",
2203
+ "null"
2204
+ ],
2205
+ "format": "date-time"
2206
+ },
2207
+ "smtp_helo": {
2208
+ "type": [
2209
+ "string",
2210
+ "null"
2211
+ ]
2212
+ },
2213
+ "smtp_mail_from": {
2214
+ "type": [
2215
+ "string",
2216
+ "null"
2217
+ ]
2218
+ },
2219
+ "smtp_rcpt_to": {
2220
+ "type": [
2221
+ "array",
2222
+ "null"
2223
+ ],
2224
+ "items": {
2225
+ "type": "string"
2226
+ }
2227
+ },
2228
+ "from_header": {
2229
+ "type": [
2230
+ "string",
2231
+ "null"
2232
+ ]
2233
+ },
2234
+ "content_discarded_at": {
2235
+ "type": [
2236
+ "string",
2237
+ "null"
2238
+ ],
2239
+ "format": "date-time"
2240
+ },
2241
+ "content_discarded_by_delivery_id": {
2242
+ "type": [
2243
+ "string",
2244
+ "null"
2245
+ ]
2246
+ },
2247
+ "from_email": {
2248
+ "type": "string",
2249
+ "description": "Parsed from address (from_header or sender fallback)"
2250
+ },
2251
+ "to_email": {
2252
+ "type": "string",
2253
+ "description": "Parsed to address (same as recipient)"
2254
+ }
2255
+ },
2256
+ "required": [
2257
+ "id",
2258
+ "sender",
2259
+ "recipient",
2260
+ "status",
2261
+ "domain",
2262
+ "created_at",
2263
+ "received_at",
2264
+ "webhook_attempt_count",
2265
+ "from_email",
2266
+ "to_email"
2267
+ ]
2268
+ },
2269
+ "Endpoint": {
2270
+ "type": "object",
2271
+ "properties": {
2272
+ "id": {
2273
+ "type": "string",
2274
+ "format": "uuid"
2275
+ },
2276
+ "org_id": {
2277
+ "type": "string",
2278
+ "format": "uuid"
2279
+ },
2280
+ "url": {
2281
+ "type": [
2282
+ "string",
2283
+ "null"
2284
+ ]
2285
+ },
2286
+ "enabled": {
2287
+ "type": "boolean"
2288
+ },
2289
+ "domain_id": {
2290
+ "type": [
2291
+ "string",
2292
+ "null"
2293
+ ],
2294
+ "format": "uuid",
2295
+ "description": "Restrict this endpoint to emails from a specific domain"
2296
+ },
2297
+ "rules": {
2298
+ "type": "object",
2299
+ "description": "Endpoint-specific filtering rules"
2300
+ },
2301
+ "created_at": {
2302
+ "type": "string",
2303
+ "format": "date-time"
2304
+ },
2305
+ "updated_at": {
2306
+ "type": "string",
2307
+ "format": "date-time"
2308
+ },
2309
+ "delivery_count": {
2310
+ "type": "integer",
2311
+ "description": "Total webhook deliveries attempted"
2312
+ },
2313
+ "success_count": {
2314
+ "type": "integer",
2315
+ "description": "Successful deliveries"
2316
+ },
2317
+ "failure_count": {
2318
+ "type": "integer",
2319
+ "description": "Failed deliveries"
2320
+ },
2321
+ "consecutive_fails": {
2322
+ "type": "integer",
2323
+ "description": "Current streak of consecutive failures"
2324
+ },
2325
+ "last_delivery_at": {
2326
+ "type": [
2327
+ "string",
2328
+ "null"
2329
+ ],
2330
+ "format": "date-time"
2331
+ },
2332
+ "last_success_at": {
2333
+ "type": [
2334
+ "string",
2335
+ "null"
2336
+ ],
2337
+ "format": "date-time"
2338
+ },
2339
+ "last_failure_at": {
2340
+ "type": [
2341
+ "string",
2342
+ "null"
2343
+ ],
2344
+ "format": "date-time"
2345
+ },
2346
+ "deactivated_at": {
2347
+ "type": [
2348
+ "string",
2349
+ "null"
2350
+ ],
2351
+ "format": "date-time"
2352
+ }
2353
+ },
2354
+ "required": [
2355
+ "id",
2356
+ "org_id",
2357
+ "enabled",
2358
+ "rules",
2359
+ "created_at",
2360
+ "updated_at",
2361
+ "delivery_count",
2362
+ "success_count",
2363
+ "failure_count",
2364
+ "consecutive_fails"
2365
+ ]
2366
+ },
2367
+ "CreateEndpointInput": {
2368
+ "type": "object",
2369
+ "additionalProperties": false,
2370
+ "properties": {
2371
+ "url": {
2372
+ "type": "string",
2373
+ "minLength": 1,
2374
+ "description": "The webhook URL to deliver events to"
2375
+ },
2376
+ "enabled": {
2377
+ "type": "boolean",
2378
+ "default": true,
2379
+ "description": "Whether the endpoint is active"
2380
+ },
2381
+ "domain_id": {
2382
+ "type": [
2383
+ "string",
2384
+ "null"
2385
+ ],
2386
+ "format": "uuid",
2387
+ "description": "Restrict to emails from a specific domain"
2388
+ },
2389
+ "rules": {
2390
+ "type": "object",
2391
+ "description": "Endpoint-specific filtering rules"
2392
+ }
2393
+ },
2394
+ "required": [
2395
+ "url"
2396
+ ]
2397
+ },
2398
+ "UpdateEndpointInput": {
2399
+ "type": "object",
2400
+ "additionalProperties": false,
2401
+ "properties": {
2402
+ "url": {
2403
+ "type": "string",
2404
+ "minLength": 1,
2405
+ "description": "New webhook URL (triggers endpoint rotation)"
2406
+ },
2407
+ "enabled": {
2408
+ "type": "boolean"
2409
+ },
2410
+ "domain_id": {
2411
+ "type": [
2412
+ "string",
2413
+ "null"
2414
+ ],
2415
+ "format": "uuid"
2416
+ },
2417
+ "rules": {
2418
+ "type": "object"
2419
+ }
2420
+ },
2421
+ "minProperties": 1
2422
+ },
2423
+ "TestResult": {
2424
+ "type": "object",
2425
+ "properties": {
2426
+ "status": {
2427
+ "type": "integer",
2428
+ "description": "HTTP status code returned by the endpoint"
2429
+ },
2430
+ "body": {
2431
+ "type": "string",
2432
+ "description": "Response body (truncated to 1000 characters)"
2433
+ },
2434
+ "signature": {
2435
+ "type": "string",
2436
+ "description": "The signature header value sent (if webhook secret is configured)"
2437
+ }
2438
+ },
2439
+ "required": [
2440
+ "status",
2441
+ "body"
2442
+ ]
2443
+ },
2444
+ "Filter": {
2445
+ "type": "object",
2446
+ "properties": {
2447
+ "id": {
2448
+ "type": "string",
2449
+ "format": "uuid"
2450
+ },
2451
+ "org_id": {
2452
+ "type": "string",
2453
+ "format": "uuid"
2454
+ },
2455
+ "domain_id": {
2456
+ "type": [
2457
+ "string",
2458
+ "null"
2459
+ ],
2460
+ "format": "uuid",
2461
+ "description": "If set, filter applies only to this domain"
2462
+ },
2463
+ "type": {
2464
+ "type": "string",
2465
+ "enum": [
2466
+ "whitelist",
2467
+ "blocklist"
2468
+ ]
2469
+ },
2470
+ "pattern": {
2471
+ "type": "string",
2472
+ "description": "Email address or pattern to match (stored lowercase)"
2473
+ },
2474
+ "enabled": {
2475
+ "type": "boolean"
2476
+ },
2477
+ "created_at": {
2478
+ "type": "string",
2479
+ "format": "date-time"
2480
+ }
2481
+ },
2482
+ "required": [
2483
+ "id",
2484
+ "org_id",
2485
+ "type",
2486
+ "pattern",
2487
+ "enabled",
2488
+ "created_at"
2489
+ ]
2490
+ },
2491
+ "CreateFilterInput": {
2492
+ "type": "object",
2493
+ "additionalProperties": false,
2494
+ "properties": {
2495
+ "type": {
2496
+ "type": "string",
2497
+ "enum": [
2498
+ "whitelist",
2499
+ "blocklist"
2500
+ ]
2501
+ },
2502
+ "pattern": {
2503
+ "type": "string",
2504
+ "minLength": 1,
2505
+ "maxLength": 500,
2506
+ "description": "Email address or pattern to filter"
2507
+ },
2508
+ "domain_id": {
2509
+ "type": [
2510
+ "string",
2511
+ "null"
2512
+ ],
2513
+ "format": "uuid",
2514
+ "description": "Restrict filter to a specific domain (Pro plan required)"
2515
+ }
2516
+ },
2517
+ "required": [
2518
+ "type",
2519
+ "pattern"
2520
+ ]
2521
+ },
2522
+ "UpdateFilterInput": {
2523
+ "type": "object",
2524
+ "additionalProperties": false,
2525
+ "properties": {
2526
+ "enabled": {
2527
+ "type": "boolean"
2528
+ }
2529
+ },
2530
+ "required": [
2531
+ "enabled"
2532
+ ]
2533
+ },
2534
+ "DeliverySummary": {
2535
+ "type": "object",
2536
+ "properties": {
2537
+ "id": {
2538
+ "type": "string",
2539
+ "description": "Delivery ID (numeric string)"
2540
+ },
2541
+ "email_id": {
2542
+ "type": "string",
2543
+ "format": "uuid"
2544
+ },
2545
+ "org_id": {
2546
+ "type": "string",
2547
+ "format": "uuid"
2548
+ },
2549
+ "endpoint_id": {
2550
+ "type": "string",
2551
+ "format": "uuid"
2552
+ },
2553
+ "endpoint_url": {
2554
+ "type": "string"
2555
+ },
2556
+ "status": {
2557
+ "type": "string",
2558
+ "enum": [
2559
+ "pending",
2560
+ "delivered",
2561
+ "header_confirmed",
2562
+ "failed"
2563
+ ]
2564
+ },
2565
+ "attempt_count": {
2566
+ "type": "integer"
2567
+ },
2568
+ "duration_ms": {
2569
+ "type": [
2570
+ "integer",
2571
+ "null"
2572
+ ]
2573
+ },
2574
+ "last_error": {
2575
+ "type": [
2576
+ "string",
2577
+ "null"
2578
+ ]
2579
+ },
2580
+ "created_at": {
2581
+ "type": "string",
2582
+ "format": "date-time"
2583
+ },
2584
+ "updated_at": {
2585
+ "type": "string",
2586
+ "format": "date-time"
2587
+ },
2588
+ "email": {
2589
+ "type": [
2590
+ "object",
2591
+ "null"
2592
+ ],
2593
+ "properties": {
2594
+ "sender": {
2595
+ "type": "string"
2596
+ },
2597
+ "recipient": {
2598
+ "type": "string"
2599
+ },
2600
+ "subject": {
2601
+ "type": [
2602
+ "string",
2603
+ "null"
2604
+ ]
2605
+ }
2606
+ },
2607
+ "required": [
2608
+ "sender",
2609
+ "recipient"
2610
+ ]
2611
+ }
2612
+ },
2613
+ "required": [
2614
+ "id",
2615
+ "email_id",
2616
+ "org_id",
2617
+ "endpoint_id",
2618
+ "endpoint_url",
2619
+ "status",
2620
+ "attempt_count",
2621
+ "created_at",
2622
+ "updated_at"
2623
+ ]
2624
+ },
2625
+ "ReplayResult": {
2626
+ "type": "object",
2627
+ "properties": {
2628
+ "delivered": {
2629
+ "type": "integer",
2630
+ "description": "Number of successful deliveries"
2631
+ },
2632
+ "failed": {
2633
+ "type": "integer",
2634
+ "description": "Number of failed deliveries"
2635
+ }
2636
+ },
2637
+ "required": [
2638
+ "delivered",
2639
+ "failed"
2640
+ ]
2641
+ }
2642
+ }
2643
+ }
2644
+ };