@fourier-labs/harbour-contracts 0.90.0 → 0.92.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 (36) hide show
  1. package/contract-release.json +27 -25
  2. package/dist/gen-ts/app-integrations-declaration.d.ts +8 -0
  3. package/dist/gen-ts/contract-release.d.ts +6 -6
  4. package/dist/gen-ts/contract-release.js +6 -6
  5. package/dist/gen-ts/data-grant-v2.d.ts +4 -4
  6. package/dist/gen-ts/deployment-app-registration.d.ts +20 -0
  7. package/dist/gen-ts/index.d.ts +1 -0
  8. package/dist/gen-ts/index.js +1 -0
  9. package/dist/gen-ts/integration-execute-input-rules.d.ts +230 -0
  10. package/dist/gen-ts/integration-execute-input-rules.js +70 -0
  11. package/dist/gen-ts/integration-execute-request.d.ts +19 -2
  12. package/dist/gen-ts/integration-execute-result.d.ts +49 -0
  13. package/dist/gen-ts/integration-operations.d.ts +56 -0
  14. package/dist/gen-ts/integration-operations.js +72 -0
  15. package/dist/gen-ts/mail-arrival-event.d.ts +17 -0
  16. package/dist/gen-ts/mail-arrival-event.js +3 -0
  17. package/dist/gen-ts/saas-connection.d.ts +11 -7
  18. package/gen-ts/app-integrations-declaration.ts +8 -0
  19. package/gen-ts/contract-release.ts +6 -6
  20. package/gen-ts/data-grant-v2.ts +18 -2
  21. package/gen-ts/deployment-app-registration.ts +20 -0
  22. package/gen-ts/index.ts +1 -0
  23. package/gen-ts/integration-execute-input-rules.ts +70 -0
  24. package/gen-ts/integration-execute-request.ts +27 -2
  25. package/gen-ts/integration-execute-result.ts +49 -0
  26. package/gen-ts/integration-operations.ts +72 -0
  27. package/gen-ts/mail-arrival-event.ts +20 -0
  28. package/gen-ts/saas-connection.ts +33 -4
  29. package/package.json +1 -1
  30. package/schema/app-integrations-declaration.schema.json +121 -9
  31. package/schema/data-grant-v2.schema.json +2 -2
  32. package/schema/deployment-app-registration.schema.json +199 -17
  33. package/schema/integration-execute-request.schema.json +458 -7
  34. package/schema/integration-execute-result.schema.json +234 -2
  35. package/schema/mail-arrival-event.schema.json +75 -0
  36. package/schema/saas-connection.schema.json +720 -138
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://schemas.harbour.dev/integration-execute-result.schema.json",
4
4
  "title": "IntegrationExecuteResult",
5
- "description": "The `data` member of a successful execute response `{data, requestId}`; the SDK unwraps `data`. Exactly one of the operation results (eighty-one operations since 0.86.0, ADR 0018, ADR 0022, ADR 0024, ADR 0028, ADR 0029 and ADR 0032): slack.channel.history -> {messages, hasMore, nextCursor?} (plain text/IDs/time, plus the display name and emoji icon a per-app-named app post carries \u2014 authorName/authorIcon, 0.55.0 \u2014 never the provider payload; `nextCursor` when hasMore, to pass as the next read's `cursor`); slack.message.post, slack.message.update, slack.message.delete -> {messageId} (the Slack ts); slack.dm.send -> {messageId, channelId} (the ts and the DM channel it went to); slack.user.lookup -> {userId, displayName}; gmail.thread.list -> {threads, hasMore, nextPageToken?} (newest matching threads; each `messageId` is the thread's newest message, what gmail.message.read takes; `nextPageToken` when hasMore); gmail.thread.read -> {id, emails} (every message of the thread, oldest first, each shaped like a gmail.message.read result); gmail.message.read -> {id, threadId, subject, from, to, date, text, truncated, attachments?} (headers plus the plain text with html stripped, cut at 16 KiB with `truncated` set; `attachments` lists the files without their bytes, which gmail.attachment.read fetches by id); gmail.attachment.read -> {filename, mimeType, size, dataBase64}; gmail.message.send -> {messageId, threadId} (the Gmail ids of the sent message and its thread); warehouse.view.read -> {rows, truncated} (rows keyed by the selected columns, or for an aggregate read by the groupBy columns then the aggregate aliases; decimal/64-bit values as strings, dates as ISO strings; since 0.70.0 every server sends `truncated`, true when more rows matched than `limit` \u2014 the server reads limit+1 rows to know \u2014 so the rows are incomplete and must not be totalled or averaged; a server before 0.70.0 omitted it). Basecamp (0.76.0, ADR 0022; every id a string of digits, every page at most 100 items, `nextPageToken` only when hasMore, text cut at 16 KiB like Gmail): basecamp.project.list -> {projects, hasMore, nextPageToken?}; basecamp.todolist.list -> {todolists, hasMore, nextPageToken?}; basecamp.todo.list -> {todos, hasMore, nextPageToken?}; basecamp.message.list -> {boardMessages, hasMore, nextPageToken?} (`boardMessages`, not `messages`, which is the Slack history shape); basecamp.comment.list -> {comments, hasMore, nextPageToken?}; basecamp.people.list -> {people, hasMore, nextPageToken?}; basecamp.todo.create, basecamp.message.post, basecamp.comment.post, basecamp.campfire.post -> {id, appUrl} (the new recording and its Basecamp page); basecamp.todo.complete -> {id, completed} with completed always true. SharePoint (0.78.0, ADR 0024; every id a Microsoft Graph id, `nextPageToken` only when hasMore and up to 2048 characters for a Graph `$skiptoken`): sharepoint.list.list -> {lists, hasMore, nextPageToken?} (at most 100, hidden lists dropped); sharepoint.item.list -> {items, hasMore, nextPageToken?} (at most 50; each item's `fields` are the column values as Graph returns them with the system columns dropped, the whole object cut at 16 KiB); sharepoint.drive.list -> {drives, hasMore, nextPageToken?}; sharepoint.file.list and sharepoint.file.search -> {files, hasMore, nextPageToken?} (at most 100 entries, folders included with isFolder); sharepoint.file.read -> {id, filename, mimeType, size, dataBase64} (the Gmail attachment shape plus the file's id; at most 2 MiB of bytes); sharepoint.item.create -> {id, webUrl} (the new item and its SharePoint page); sharepoint.item.update -> {id}; sharepoint.file.upload -> {id, filename, webUrl} (the stored file, under the name `conflict` rename may have given it). Google Workspace and Microsoft 365 (0.86.0, ADR 0032; every page at most 100 items, `nextPageToken` only when hasMore, up to 2048 characters): calendar.calendar.list -> {calendars, hasMore, nextPageToken?}; calendar.event.list and outlook.event.list -> {events, hasMore, nextPageToken?}; calendar.event.read and outlook.event.read -> the event itself, {id, title, start, end, allDay, timeZone?, location?, description?, attendees, organizer?, webUrl, eventStatus} (the CalendarEvent shape spread at the top level, the way gmail.message.read spreads GmailMessage; `eventStatus`, because `status` is the HTTP status of a service.request result); calendar.event.create and outlook.event.create -> {id, webUrl}; calendar.event.update, calendar.event.cancel, outlook.event.update, outlook.event.cancel -> {id}; drive.file.list, drive.file.search, onedrive.file.list, onedrive.file.search -> {files, hasMore, nextPageToken?} (the SharePoint entry shape); drive.file.read and onedrive.file.read -> {id, filename, mimeType, size, dataBase64} (the sharepoint.file.read shape; `filename` and `mimeType` are the stored or exported name and type); drive.file.upload and onedrive.file.upload -> {id, filename, webUrl}; drive.folder.create and onedrive.folder.create -> {id, webUrl}; docs.document.read -> {id, title, text, revisionId, webUrl, truncated} (the body as plain text cut at 64 KiB); docs.document.create -> {id, webUrl}; docs.document.append -> {id, revisionId}; docs.text.replace -> {id, replaced} (how many occurrences changed); sheets.spreadsheet.read -> {id, title, sheets, webUrl}; sheets.values.read -> {range, values} (the range Google answered with and the cells as rows; a trailing empty row or cell is dropped by Google); sheets.values.append -> {range, updatedRows}; sheets.values.update -> {range, updatedCells}; sheets.values.clear -> {range}; sheets.spreadsheet.create -> {id, webUrl}; outlook.message.list -> {mailMessages, hasMore, nextPageToken?} (`mailMessages`, not `messages`, which is the Slack history shape); outlook.message.read -> the gmail.message.read shape {id, threadId, subject, from, to, date, text, truncated, attachments?} with `threadId` the Graph conversationId; outlook.attachment.read -> {id, filename, mimeType, size, dataBase64}; outlook.message.send and outlook.message.reply -> {id} (the sent message's id, which Graph does not return for a send: governance's own internetMessageId); excel.worksheet.list -> {worksheets}; excel.range.read -> {address, values}; excel.range.update -> {address, updatedCells}; excel.table.list -> {tables}; excel.row.add -> {index, updatedRows} (the index of the first added row); excel.worksheet.add -> {id, name}; teams.message.list -> {teamsMessages, hasMore, nextPageToken?} (`teamsMessages`, apart from Slack's `messages`); teams.message.post and teams.reply.post -> {id, webUrl}. An unknown send outcome is never a result; it is an error with details.code SEND_OUTCOME_UNKNOWN. A whole result over 1 MiB is rejected, never cut down to fit \u2014 except a gmail.attachment.read, a sharepoint.file.read, a drive.file.read, an onedrive.file.read or an outlook.attachment.read, whose single file is bounded by its own 2 MiB.",
5
+ "description": "The `data` member of a successful execute response `{data, requestId}`; the SDK unwraps `data`. Exactly one of the operation results (eighty-one operations since 0.86.0, ADR 0018, ADR 0022, ADR 0024, ADR 0028, ADR 0029 and ADR 0032): slack.channel.history -> {messages, hasMore, nextCursor?} (plain text/IDs/time, plus the display name and emoji icon a per-app-named app post carries — authorName/authorIcon, 0.55.0 — never the provider payload; `nextCursor` when hasMore, to pass as the next read's `cursor`); slack.message.post, slack.message.update, slack.message.delete -> {messageId} (the Slack ts); slack.dm.send -> {messageId, channelId} (the ts and the DM channel it went to); slack.user.lookup -> {userId, displayName}; gmail.thread.list -> {threads, hasMore, nextPageToken?} (newest matching threads; each `messageId` is the thread's newest message, what gmail.message.read takes; `nextPageToken` when hasMore); gmail.thread.read -> {id, emails} (every message of the thread, oldest first, each shaped like a gmail.message.read result); gmail.message.read -> {id, threadId, subject, from, to, date, text, truncated, attachments?} (headers plus the plain text with html stripped, cut at 16 KiB with `truncated` set; `attachments` lists the files without their bytes, which gmail.attachment.read fetches by id); gmail.attachment.read -> {filename, mimeType, size, dataBase64}; gmail.message.send -> {messageId, threadId} (the Gmail ids of the sent message and its thread); warehouse.view.read -> {rows, truncated} (rows keyed by the selected columns, or for an aggregate read by the groupBy columns then the aggregate aliases; decimal/64-bit values as strings, dates as ISO strings; since 0.70.0 every server sends `truncated`, true when more rows matched than `limit` — the server reads limit+1 rows to know — so the rows are incomplete and must not be totalled or averaged; a server before 0.70.0 omitted it). Basecamp (0.76.0, ADR 0022; every id a string of digits, every page at most 100 items, `nextPageToken` only when hasMore, text cut at 16 KiB like Gmail): basecamp.project.list -> {projects, hasMore, nextPageToken?}; basecamp.todolist.list -> {todolists, hasMore, nextPageToken?}; basecamp.todo.list -> {todos, hasMore, nextPageToken?}; basecamp.message.list -> {boardMessages, hasMore, nextPageToken?} (`boardMessages`, not `messages`, which is the Slack history shape); basecamp.comment.list -> {comments, hasMore, nextPageToken?}; basecamp.people.list -> {people, hasMore, nextPageToken?}; basecamp.todo.create, basecamp.message.post, basecamp.comment.post, basecamp.campfire.post -> {id, appUrl} (the new recording and its Basecamp page); basecamp.todo.complete -> {id, completed} with completed always true. SharePoint (0.78.0, ADR 0024; every id a Microsoft Graph id, `nextPageToken` only when hasMore and up to 2048 characters for a Graph `$skiptoken`): sharepoint.list.list -> {lists, hasMore, nextPageToken?} (at most 100, hidden lists dropped); sharepoint.item.list -> {items, hasMore, nextPageToken?} (at most 50; each item's `fields` are the column values as Graph returns them with the system columns dropped, the whole object cut at 16 KiB); sharepoint.drive.list -> {drives, hasMore, nextPageToken?}; sharepoint.file.list and sharepoint.file.search -> {files, hasMore, nextPageToken?} (at most 100 entries, folders included with isFolder); sharepoint.file.read -> {id, filename, mimeType, size, dataBase64} (the Gmail attachment shape plus the file's id; at most 2 MiB of bytes); sharepoint.item.create -> {id, webUrl} (the new item and its SharePoint page); sharepoint.item.update -> {id}; sharepoint.file.upload -> {id, filename, webUrl} (the stored file, under the name `conflict` rename may have given it). Google Workspace and Microsoft 365 (0.86.0, ADR 0032; every page at most 100 items, `nextPageToken` only when hasMore, up to 2048 characters): calendar.calendar.list -> {calendars, hasMore, nextPageToken?}; calendar.event.list and outlook.event.list -> {events, hasMore, nextPageToken?}; calendar.event.read and outlook.event.read -> the event itself, {id, title, start, end, allDay, timeZone?, location?, description?, attendees, organizer?, webUrl, eventStatus} (the CalendarEvent shape spread at the top level, the way gmail.message.read spreads GmailMessage; `eventStatus`, because `status` is the HTTP status of a service.request result); calendar.event.create and outlook.event.create -> {id, webUrl}; calendar.event.update, calendar.event.cancel, outlook.event.update, outlook.event.cancel -> {id}; drive.file.list, drive.file.search, onedrive.file.list, onedrive.file.search -> {files, hasMore, nextPageToken?} (the SharePoint entry shape); drive.file.read and onedrive.file.read -> {id, filename, mimeType, size, dataBase64} (the sharepoint.file.read shape; `filename` and `mimeType` are the stored or exported name and type); drive.file.upload and onedrive.file.upload -> {id, filename, webUrl}; drive.folder.create and onedrive.folder.create -> {id, webUrl}; docs.document.read -> {id, title, text, revisionId, webUrl, truncated} (the body as plain text cut at 64 KiB); docs.document.create -> {id, webUrl}; docs.document.append -> {id, revisionId}; docs.text.replace -> {id, replaced} (how many occurrences changed); sheets.spreadsheet.read -> {id, title, sheets, webUrl}; sheets.values.read -> {range, values} (the range Google answered with and the cells as rows; a trailing empty row or cell is dropped by Google); sheets.values.append -> {range, updatedRows}; sheets.values.update -> {range, updatedCells}; sheets.values.clear -> {range}; sheets.spreadsheet.create -> {id, webUrl}; outlook.message.list -> {mailMessages, hasMore, nextPageToken?} (`mailMessages`, not `messages`, which is the Slack history shape); outlook.message.read -> the gmail.message.read shape {id, threadId, subject, from, to, date, text, truncated, attachments?} with `threadId` the Graph conversationId; outlook.attachment.read -> {id, filename, mimeType, size, dataBase64}; outlook.message.send and outlook.message.reply -> {id} (the sent message's id, which Graph does not return for a send: governance's own internetMessageId); excel.worksheet.list -> {worksheets}; excel.range.read -> {address, values}; excel.range.update -> {address, updatedCells}; excel.table.list -> {tables}; excel.row.add -> {index, updatedRows} (the index of the first added row); excel.worksheet.add -> {id, name}; teams.message.list -> {teamsMessages, hasMore, nextPageToken?} (`teamsMessages`, apart from Slack's `messages`); teams.message.post and teams.reply.post -> {id, webUrl}. An unknown send outcome is never a result; it is an error with details.code SEND_OUTCOME_UNKNOWN. A whole result over 1 MiB is rejected, never cut down to fit — except a gmail.attachment.read, a sharepoint.file.read, a drive.file.read, an onedrive.file.read or an outlook.attachment.read, whose single file is bounded by its own 2 MiB.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "properties": {
@@ -429,7 +429,203 @@
429
429
  "type": "string"
430
430
  }
431
431
  },
432
- "body": {}
432
+ "body": {},
433
+ "booksDocument": {
434
+ "type": "object",
435
+ "additionalProperties": false,
436
+ "required": [
437
+ "id",
438
+ "number",
439
+ "status",
440
+ "currency",
441
+ "total",
442
+ "lineItems"
443
+ ],
444
+ "properties": {
445
+ "id": {
446
+ "type": "string",
447
+ "pattern": "^[0-9]{1,32}$",
448
+ "maxLength": 32
449
+ },
450
+ "number": {
451
+ "type": "string",
452
+ "maxLength": 2000
453
+ },
454
+ "status": {
455
+ "type": "string",
456
+ "maxLength": 2000
457
+ },
458
+ "currency": {
459
+ "type": "string",
460
+ "maxLength": 2000
461
+ },
462
+ "total": {
463
+ "type": "string",
464
+ "maxLength": 64
465
+ },
466
+ "vendorId": {
467
+ "type": "string",
468
+ "pattern": "^[0-9]{1,32}$",
469
+ "maxLength": 32
470
+ },
471
+ "customerId": {
472
+ "type": "string",
473
+ "pattern": "^[0-9]{1,32}$",
474
+ "maxLength": 32
475
+ },
476
+ "lineItems": {
477
+ "type": "array",
478
+ "maxItems": 100,
479
+ "items": {
480
+ "type": "object",
481
+ "additionalProperties": false,
482
+ "required": [
483
+ "itemId",
484
+ "quantity",
485
+ "rate",
486
+ "total"
487
+ ],
488
+ "properties": {
489
+ "itemId": {
490
+ "type": "string",
491
+ "pattern": "^[0-9]{1,32}$",
492
+ "maxLength": 32
493
+ },
494
+ "quantity": {
495
+ "type": "string",
496
+ "maxLength": 64
497
+ },
498
+ "rate": {
499
+ "type": "string",
500
+ "maxLength": 64
501
+ },
502
+ "total": {
503
+ "type": "string",
504
+ "maxLength": 64
505
+ },
506
+ "description": {
507
+ "type": "string",
508
+ "maxLength": 2000
509
+ }
510
+ }
511
+ }
512
+ }
513
+ }
514
+ },
515
+ "booksContacts": {
516
+ "type": "array",
517
+ "maxItems": 100,
518
+ "items": {
519
+ "type": "object",
520
+ "additionalProperties": false,
521
+ "required": [
522
+ "id",
523
+ "name",
524
+ "type"
525
+ ],
526
+ "properties": {
527
+ "id": {
528
+ "type": "string",
529
+ "pattern": "^[0-9]{1,32}$",
530
+ "maxLength": 32
531
+ },
532
+ "name": {
533
+ "type": "string",
534
+ "maxLength": 2000
535
+ },
536
+ "type": {
537
+ "type": "string",
538
+ "maxLength": 2000
539
+ }
540
+ }
541
+ }
542
+ },
543
+ "booksPurchaseOrders": {
544
+ "type": "array",
545
+ "maxItems": 100,
546
+ "items": {
547
+ "type": "object",
548
+ "additionalProperties": false,
549
+ "required": [
550
+ "id",
551
+ "number",
552
+ "status",
553
+ "currency",
554
+ "total",
555
+ "lineItems"
556
+ ],
557
+ "properties": {
558
+ "id": {
559
+ "type": "string",
560
+ "pattern": "^[0-9]{1,32}$",
561
+ "maxLength": 32
562
+ },
563
+ "number": {
564
+ "type": "string",
565
+ "maxLength": 2000
566
+ },
567
+ "status": {
568
+ "type": "string",
569
+ "maxLength": 2000
570
+ },
571
+ "currency": {
572
+ "type": "string",
573
+ "maxLength": 2000
574
+ },
575
+ "total": {
576
+ "type": "string",
577
+ "maxLength": 64
578
+ },
579
+ "vendorId": {
580
+ "type": "string",
581
+ "pattern": "^[0-9]{1,32}$",
582
+ "maxLength": 32
583
+ },
584
+ "customerId": {
585
+ "type": "string",
586
+ "pattern": "^[0-9]{1,32}$",
587
+ "maxLength": 32
588
+ },
589
+ "lineItems": {
590
+ "type": "array",
591
+ "maxItems": 100,
592
+ "items": {
593
+ "type": "object",
594
+ "additionalProperties": false,
595
+ "required": [
596
+ "itemId",
597
+ "quantity",
598
+ "rate",
599
+ "total"
600
+ ],
601
+ "properties": {
602
+ "itemId": {
603
+ "type": "string",
604
+ "pattern": "^[0-9]{1,32}$",
605
+ "maxLength": 32
606
+ },
607
+ "quantity": {
608
+ "type": "string",
609
+ "maxLength": 64
610
+ },
611
+ "rate": {
612
+ "type": "string",
613
+ "maxLength": 64
614
+ },
615
+ "total": {
616
+ "type": "string",
617
+ "maxLength": 64
618
+ },
619
+ "description": {
620
+ "type": "string",
621
+ "maxLength": 2000
622
+ }
623
+ }
624
+ }
625
+ }
626
+ }
627
+ }
628
+ }
433
629
  },
434
630
  "oneOf": [
435
631
  {
@@ -891,6 +1087,42 @@
891
1087
  "nextPageToken"
892
1088
  ]
893
1089
  }
1090
+ },
1091
+ {
1092
+ "required": [
1093
+ "booksContacts",
1094
+ "hasMore"
1095
+ ],
1096
+ "propertyNames": {
1097
+ "enum": [
1098
+ "booksContacts",
1099
+ "hasMore",
1100
+ "nextPageToken"
1101
+ ]
1102
+ }
1103
+ },
1104
+ {
1105
+ "required": [
1106
+ "booksPurchaseOrders",
1107
+ "hasMore"
1108
+ ],
1109
+ "propertyNames": {
1110
+ "enum": [
1111
+ "booksPurchaseOrders",
1112
+ "hasMore",
1113
+ "nextPageToken"
1114
+ ]
1115
+ }
1116
+ },
1117
+ {
1118
+ "required": [
1119
+ "booksDocument"
1120
+ ],
1121
+ "propertyNames": {
1122
+ "enum": [
1123
+ "booksDocument"
1124
+ ]
1125
+ }
894
1126
  }
895
1127
  ],
896
1128
  "definitions": {
@@ -0,0 +1,75 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://contracts.harbour.dev/mail-arrival-event.schema.json",
4
+ "title": "MailArrivalEvent",
5
+ "description": "IAM-authorized invocation of a deployed app Event Function. The delivery ID is stable across notification retries; runtime deduplicates before running the declared job. No tokens or message bodies.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "mailArrival"
10
+ ],
11
+ "properties": {
12
+ "mailArrival": {
13
+ "type": "object",
14
+ "additionalProperties": false,
15
+ "required": [
16
+ "deliveryId",
17
+ "job",
18
+ "owner",
19
+ "input"
20
+ ],
21
+ "properties": {
22
+ "deliveryId": {
23
+ "type": "string",
24
+ "pattern": "^[a-f0-9]{64}$"
25
+ },
26
+ "job": {
27
+ "type": "string",
28
+ "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$"
29
+ },
30
+ "owner": {
31
+ "type": "string",
32
+ "minLength": 1,
33
+ "maxLength": 320
34
+ },
35
+ "input": {
36
+ "type": "object",
37
+ "additionalProperties": false,
38
+ "required": [
39
+ "provider",
40
+ "sourceId",
41
+ "messageId",
42
+ "threadId",
43
+ "receivedAt"
44
+ ],
45
+ "properties": {
46
+ "provider": {
47
+ "enum": [
48
+ "gmail",
49
+ "outlook"
50
+ ]
51
+ },
52
+ "sourceId": {
53
+ "type": "string",
54
+ "minLength": 1,
55
+ "maxLength": 64
56
+ },
57
+ "messageId": {
58
+ "type": "string",
59
+ "minLength": 1,
60
+ "maxLength": 2048
61
+ },
62
+ "threadId": {
63
+ "type": "string",
64
+ "maxLength": 2048
65
+ },
66
+ "receivedAt": {
67
+ "type": "string",
68
+ "format": "date-time"
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }