@roundtable-bb/sdk 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.
package/openapi.json ADDED
@@ -0,0 +1,3880 @@
1
+ {
2
+ "openapi": "3.0.3",
3
+ "info": {
4
+ "title": "Roundtable API",
5
+ "version": "1.0.0",
6
+ "description": "Headless forum CMS — multi-tenant REST API"
7
+ },
8
+ "components": {
9
+ "securitySchemes": {
10
+ "bearerAuth": {
11
+ "type": "http",
12
+ "scheme": "bearer",
13
+ "bearerFormat": "JWT"
14
+ },
15
+ "apiKey": {
16
+ "type": "apiKey",
17
+ "in": "header",
18
+ "name": "X-API-Key"
19
+ }
20
+ },
21
+ "schemas": {}
22
+ },
23
+ "paths": {
24
+ "/auth/{*}": {
25
+ "get": {
26
+ "parameters": [
27
+ {
28
+ "schema": {
29
+ "type": "string"
30
+ },
31
+ "in": "path",
32
+ "name": "*",
33
+ "required": true
34
+ }
35
+ ],
36
+ "responses": {
37
+ "200": {
38
+ "description": "Default Response"
39
+ }
40
+ }
41
+ },
42
+ "head": {
43
+ "parameters": [
44
+ {
45
+ "schema": {
46
+ "type": "string"
47
+ },
48
+ "in": "path",
49
+ "name": "*",
50
+ "required": true
51
+ }
52
+ ],
53
+ "responses": {
54
+ "200": {
55
+ "description": "Default Response"
56
+ }
57
+ }
58
+ },
59
+ "trace": {
60
+ "parameters": [
61
+ {
62
+ "schema": {
63
+ "type": "string"
64
+ },
65
+ "in": "path",
66
+ "name": "*",
67
+ "required": true
68
+ }
69
+ ],
70
+ "responses": {
71
+ "200": {
72
+ "description": "Default Response"
73
+ }
74
+ }
75
+ },
76
+ "delete": {
77
+ "parameters": [
78
+ {
79
+ "schema": {
80
+ "type": "string"
81
+ },
82
+ "in": "path",
83
+ "name": "*",
84
+ "required": true
85
+ }
86
+ ],
87
+ "responses": {
88
+ "200": {
89
+ "description": "Default Response"
90
+ }
91
+ }
92
+ },
93
+ "options": {
94
+ "parameters": [
95
+ {
96
+ "schema": {
97
+ "type": "string"
98
+ },
99
+ "in": "path",
100
+ "name": "*",
101
+ "required": true
102
+ }
103
+ ],
104
+ "responses": {
105
+ "200": {
106
+ "description": "Default Response"
107
+ }
108
+ }
109
+ },
110
+ "patch": {
111
+ "parameters": [
112
+ {
113
+ "schema": {
114
+ "type": "string"
115
+ },
116
+ "in": "path",
117
+ "name": "*",
118
+ "required": true
119
+ }
120
+ ],
121
+ "responses": {
122
+ "200": {
123
+ "description": "Default Response"
124
+ }
125
+ }
126
+ },
127
+ "put": {
128
+ "parameters": [
129
+ {
130
+ "schema": {
131
+ "type": "string"
132
+ },
133
+ "in": "path",
134
+ "name": "*",
135
+ "required": true
136
+ }
137
+ ],
138
+ "responses": {
139
+ "200": {
140
+ "description": "Default Response"
141
+ }
142
+ }
143
+ },
144
+ "post": {
145
+ "parameters": [
146
+ {
147
+ "schema": {
148
+ "type": "string"
149
+ },
150
+ "in": "path",
151
+ "name": "*",
152
+ "required": true
153
+ }
154
+ ],
155
+ "responses": {
156
+ "200": {
157
+ "description": "Default Response"
158
+ }
159
+ }
160
+ }
161
+ },
162
+ "/tenants": {
163
+ "post": {
164
+ "operationId": "createTenant",
165
+ "summary": "Create a new tenant (requires allow_tenant_creation platform setting or platform owner)",
166
+ "tags": [
167
+ "tenants"
168
+ ],
169
+ "requestBody": {
170
+ "required": true,
171
+ "content": {
172
+ "application/json": {
173
+ "schema": {
174
+ "type": "object",
175
+ "properties": {
176
+ "name": {
177
+ "type": "string",
178
+ "minLength": 1,
179
+ "maxLength": 100
180
+ },
181
+ "hostname": {
182
+ "type": "string",
183
+ "minLength": 1
184
+ }
185
+ },
186
+ "required": [
187
+ "name",
188
+ "hostname"
189
+ ]
190
+ }
191
+ }
192
+ }
193
+ },
194
+ "security": [
195
+ {
196
+ "bearerAuth": []
197
+ }
198
+ ],
199
+ "responses": {
200
+ "201": {
201
+ "description": "Default Response",
202
+ "content": {
203
+ "application/json": {
204
+ "schema": {
205
+ "type": "object",
206
+ "properties": {
207
+ "id": {
208
+ "type": "integer",
209
+ "minimum": -9007199254740991,
210
+ "maximum": 9007199254740991
211
+ },
212
+ "name": {
213
+ "type": "string"
214
+ },
215
+ "createdAt": {
216
+ "type": "string"
217
+ },
218
+ "hostnames": {
219
+ "type": "array",
220
+ "items": {
221
+ "type": "object",
222
+ "properties": {
223
+ "hostname": {
224
+ "type": "string"
225
+ },
226
+ "isPrimary": {
227
+ "type": "boolean"
228
+ }
229
+ },
230
+ "required": [
231
+ "hostname",
232
+ "isPrimary"
233
+ ],
234
+ "additionalProperties": false
235
+ }
236
+ }
237
+ },
238
+ "required": [
239
+ "id",
240
+ "name",
241
+ "createdAt",
242
+ "hostnames"
243
+ ],
244
+ "additionalProperties": false
245
+ }
246
+ }
247
+ }
248
+ }
249
+ }
250
+ }
251
+ },
252
+ "/platform/settings": {
253
+ "get": {
254
+ "operationId": "listPlatformSettings",
255
+ "summary": "List all platform settings (platform owners only)",
256
+ "tags": [
257
+ "platform"
258
+ ],
259
+ "security": [
260
+ {
261
+ "bearerAuth": []
262
+ }
263
+ ],
264
+ "responses": {
265
+ "200": {
266
+ "description": "Default Response",
267
+ "content": {
268
+ "application/json": {
269
+ "schema": {
270
+ "type": "array",
271
+ "items": {
272
+ "type": "object",
273
+ "properties": {
274
+ "key": {
275
+ "type": "string"
276
+ },
277
+ "value": {},
278
+ "updatedAt": {
279
+ "type": "string",
280
+ "format": "date-time",
281
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
282
+ }
283
+ },
284
+ "required": [
285
+ "key",
286
+ "value",
287
+ "updatedAt"
288
+ ],
289
+ "additionalProperties": false
290
+ }
291
+ }
292
+ }
293
+ }
294
+ }
295
+ }
296
+ }
297
+ },
298
+ "/platform/settings/{key}": {
299
+ "patch": {
300
+ "operationId": "updatePlatformSetting",
301
+ "summary": "Update a platform setting (platform owners only)",
302
+ "tags": [
303
+ "platform"
304
+ ],
305
+ "requestBody": {
306
+ "required": true,
307
+ "content": {
308
+ "application/json": {
309
+ "schema": {
310
+ "type": "object",
311
+ "properties": {
312
+ "value": {}
313
+ },
314
+ "required": [
315
+ "value"
316
+ ]
317
+ }
318
+ }
319
+ }
320
+ },
321
+ "parameters": [
322
+ {
323
+ "schema": {
324
+ "type": "string"
325
+ },
326
+ "in": "path",
327
+ "name": "key",
328
+ "required": true
329
+ }
330
+ ],
331
+ "security": [
332
+ {
333
+ "bearerAuth": []
334
+ }
335
+ ],
336
+ "responses": {
337
+ "200": {
338
+ "description": "Default Response",
339
+ "content": {
340
+ "application/json": {
341
+ "schema": {
342
+ "type": "object",
343
+ "properties": {
344
+ "key": {
345
+ "type": "string"
346
+ },
347
+ "value": {},
348
+ "updatedAt": {
349
+ "type": "string",
350
+ "format": "date-time",
351
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
352
+ }
353
+ },
354
+ "required": [
355
+ "key",
356
+ "value",
357
+ "updatedAt"
358
+ ],
359
+ "additionalProperties": false
360
+ }
361
+ }
362
+ }
363
+ }
364
+ }
365
+ }
366
+ },
367
+ "/platform/owners": {
368
+ "get": {
369
+ "operationId": "listPlatformOwners",
370
+ "summary": "List platform owner user IDs (platform owners only)",
371
+ "tags": [
372
+ "platform"
373
+ ],
374
+ "security": [
375
+ {
376
+ "bearerAuth": []
377
+ }
378
+ ],
379
+ "responses": {
380
+ "200": {
381
+ "description": "Default Response",
382
+ "content": {
383
+ "application/json": {
384
+ "schema": {
385
+ "type": "array",
386
+ "items": {
387
+ "type": "string"
388
+ }
389
+ }
390
+ }
391
+ }
392
+ }
393
+ }
394
+ },
395
+ "post": {
396
+ "operationId": "grantPlatformOwner",
397
+ "summary": "Grant platform owner status to a user (platform owners only)",
398
+ "tags": [
399
+ "platform"
400
+ ],
401
+ "requestBody": {
402
+ "required": true,
403
+ "content": {
404
+ "application/json": {
405
+ "schema": {
406
+ "type": "object",
407
+ "properties": {
408
+ "userId": {
409
+ "type": "string",
410
+ "format": "uuid",
411
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
412
+ }
413
+ },
414
+ "required": [
415
+ "userId"
416
+ ]
417
+ }
418
+ }
419
+ }
420
+ },
421
+ "security": [
422
+ {
423
+ "bearerAuth": []
424
+ }
425
+ ],
426
+ "responses": {
427
+ "204": {
428
+ "description": "Default Response",
429
+ "content": {
430
+ "application/json": {
431
+ "schema": {
432
+ "type": "string",
433
+ "nullable": true,
434
+ "enum": [
435
+ null
436
+ ]
437
+ }
438
+ }
439
+ }
440
+ }
441
+ }
442
+ }
443
+ },
444
+ "/platform/owners/{userId}": {
445
+ "delete": {
446
+ "operationId": "revokePlatformOwner",
447
+ "summary": "Revoke platform owner status from a user (platform owners only)",
448
+ "tags": [
449
+ "platform"
450
+ ],
451
+ "parameters": [
452
+ {
453
+ "schema": {
454
+ "type": "string",
455
+ "format": "uuid",
456
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
457
+ },
458
+ "in": "path",
459
+ "name": "userId",
460
+ "required": true
461
+ }
462
+ ],
463
+ "security": [
464
+ {
465
+ "bearerAuth": []
466
+ }
467
+ ],
468
+ "responses": {
469
+ "204": {
470
+ "description": "Default Response",
471
+ "content": {
472
+ "application/json": {
473
+ "schema": {
474
+ "type": "string",
475
+ "nullable": true,
476
+ "enum": [
477
+ null
478
+ ]
479
+ }
480
+ }
481
+ }
482
+ }
483
+ }
484
+ }
485
+ },
486
+ "/platform/api-keys": {
487
+ "get": {
488
+ "operationId": "listApiKeys",
489
+ "summary": "List your platform-scoped API keys",
490
+ "tags": [
491
+ "platform"
492
+ ],
493
+ "security": [
494
+ {
495
+ "bearerAuth": []
496
+ }
497
+ ],
498
+ "responses": {
499
+ "200": {
500
+ "description": "Default Response",
501
+ "content": {
502
+ "application/json": {
503
+ "schema": {
504
+ "type": "array",
505
+ "items": {
506
+ "type": "object",
507
+ "properties": {
508
+ "id": {
509
+ "type": "string",
510
+ "format": "uuid",
511
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
512
+ },
513
+ "name": {
514
+ "type": "string"
515
+ },
516
+ "lastUsedAt": {
517
+ "nullable": true,
518
+ "type": "string",
519
+ "format": "date-time",
520
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
521
+ },
522
+ "createdAt": {
523
+ "type": "string",
524
+ "format": "date-time",
525
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
526
+ }
527
+ },
528
+ "required": [
529
+ "id",
530
+ "name",
531
+ "lastUsedAt",
532
+ "createdAt"
533
+ ],
534
+ "additionalProperties": false
535
+ }
536
+ }
537
+ }
538
+ }
539
+ }
540
+ }
541
+ },
542
+ "post": {
543
+ "operationId": "createApiKey",
544
+ "summary": "Create a platform-scoped API key (returned once in full)",
545
+ "tags": [
546
+ "platform"
547
+ ],
548
+ "requestBody": {
549
+ "required": true,
550
+ "content": {
551
+ "application/json": {
552
+ "schema": {
553
+ "type": "object",
554
+ "properties": {
555
+ "name": {
556
+ "type": "string",
557
+ "minLength": 1,
558
+ "maxLength": 100
559
+ }
560
+ },
561
+ "required": [
562
+ "name"
563
+ ]
564
+ }
565
+ }
566
+ }
567
+ },
568
+ "security": [
569
+ {
570
+ "bearerAuth": []
571
+ }
572
+ ],
573
+ "responses": {
574
+ "201": {
575
+ "description": "Default Response",
576
+ "content": {
577
+ "application/json": {
578
+ "schema": {
579
+ "type": "object",
580
+ "properties": {
581
+ "id": {
582
+ "type": "string",
583
+ "format": "uuid",
584
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
585
+ },
586
+ "name": {
587
+ "type": "string"
588
+ },
589
+ "lastUsedAt": {
590
+ "nullable": true,
591
+ "type": "string",
592
+ "format": "date-time",
593
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
594
+ },
595
+ "createdAt": {
596
+ "type": "string",
597
+ "format": "date-time",
598
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
599
+ },
600
+ "key": {
601
+ "type": "string"
602
+ }
603
+ },
604
+ "required": [
605
+ "id",
606
+ "name",
607
+ "lastUsedAt",
608
+ "createdAt",
609
+ "key"
610
+ ],
611
+ "additionalProperties": false
612
+ }
613
+ }
614
+ }
615
+ }
616
+ }
617
+ }
618
+ },
619
+ "/platform/api-keys/{id}": {
620
+ "delete": {
621
+ "operationId": "deleteApiKey",
622
+ "summary": "Delete a platform-scoped API key",
623
+ "tags": [
624
+ "platform"
625
+ ],
626
+ "parameters": [
627
+ {
628
+ "schema": {
629
+ "type": "string",
630
+ "format": "uuid",
631
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
632
+ },
633
+ "in": "path",
634
+ "name": "id",
635
+ "required": true
636
+ }
637
+ ],
638
+ "security": [
639
+ {
640
+ "bearerAuth": []
641
+ }
642
+ ],
643
+ "responses": {
644
+ "204": {
645
+ "description": "Default Response",
646
+ "content": {
647
+ "application/json": {
648
+ "schema": {
649
+ "type": "string",
650
+ "nullable": true,
651
+ "enum": [
652
+ null
653
+ ]
654
+ }
655
+ }
656
+ }
657
+ }
658
+ }
659
+ }
660
+ },
661
+ "/tenant-token": {
662
+ "post": {
663
+ "operationId": "exchangeTenantToken",
664
+ "summary": "Exchange session cookie for a short-lived tenant JWT",
665
+ "tags": [
666
+ "auth"
667
+ ],
668
+ "description": "Tenant is identified from the Origin header. Returns a JWT containing { userId, tenantId, groupId, exp }.",
669
+ "responses": {
670
+ "200": {
671
+ "description": "Default Response",
672
+ "content": {
673
+ "application/json": {
674
+ "schema": {
675
+ "type": "object",
676
+ "properties": {
677
+ "token": {
678
+ "type": "string"
679
+ },
680
+ "expiresIn": {
681
+ "type": "integer",
682
+ "minimum": -9007199254740991,
683
+ "maximum": 9007199254740991
684
+ }
685
+ },
686
+ "required": [
687
+ "token",
688
+ "expiresIn"
689
+ ],
690
+ "additionalProperties": false
691
+ }
692
+ }
693
+ }
694
+ },
695
+ "401": {
696
+ "description": "Default Response",
697
+ "content": {
698
+ "application/json": {
699
+ "schema": {
700
+ "type": "object",
701
+ "properties": {
702
+ "code": {
703
+ "type": "string"
704
+ },
705
+ "message": {
706
+ "type": "string"
707
+ }
708
+ },
709
+ "required": [
710
+ "code",
711
+ "message"
712
+ ],
713
+ "additionalProperties": false
714
+ }
715
+ }
716
+ }
717
+ },
718
+ "404": {
719
+ "description": "Default Response",
720
+ "content": {
721
+ "application/json": {
722
+ "schema": {
723
+ "type": "object",
724
+ "properties": {
725
+ "code": {
726
+ "type": "string"
727
+ },
728
+ "message": {
729
+ "type": "string"
730
+ }
731
+ },
732
+ "required": [
733
+ "code",
734
+ "message"
735
+ ],
736
+ "additionalProperties": false
737
+ }
738
+ }
739
+ }
740
+ }
741
+ }
742
+ }
743
+ },
744
+ "/notifications": {
745
+ "get": {
746
+ "operationId": "listNotifications",
747
+ "summary": "Get unified notification inbox",
748
+ "tags": [
749
+ "notifications"
750
+ ],
751
+ "parameters": [
752
+ {
753
+ "schema": {
754
+ "type": "string",
755
+ "format": "uuid",
756
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
757
+ },
758
+ "in": "query",
759
+ "name": "cursor",
760
+ "required": false
761
+ },
762
+ {
763
+ "schema": {
764
+ "default": 30,
765
+ "type": "integer",
766
+ "minimum": 1,
767
+ "maximum": 100
768
+ },
769
+ "in": "query",
770
+ "name": "limit",
771
+ "required": false
772
+ },
773
+ {
774
+ "schema": {
775
+ "default": false,
776
+ "type": "boolean"
777
+ },
778
+ "in": "query",
779
+ "name": "unreadOnly",
780
+ "required": false
781
+ }
782
+ ],
783
+ "security": [
784
+ {
785
+ "bearerAuth": []
786
+ }
787
+ ],
788
+ "responses": {
789
+ "200": {
790
+ "description": "Default Response",
791
+ "content": {
792
+ "application/json": {
793
+ "schema": {
794
+ "type": "object",
795
+ "properties": {
796
+ "items": {
797
+ "type": "array",
798
+ "items": {
799
+ "type": "object",
800
+ "properties": {
801
+ "id": {
802
+ "type": "string",
803
+ "format": "uuid",
804
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
805
+ },
806
+ "userId": {
807
+ "type": "string",
808
+ "format": "uuid",
809
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
810
+ },
811
+ "tenantId": {
812
+ "type": "integer",
813
+ "minimum": -9007199254740991,
814
+ "maximum": 9007199254740991
815
+ },
816
+ "type": {
817
+ "type": "string",
818
+ "enum": [
819
+ "new_post"
820
+ ]
821
+ },
822
+ "payload": {
823
+ "type": "object",
824
+ "additionalProperties": {}
825
+ },
826
+ "readAt": {
827
+ "nullable": true,
828
+ "type": "string",
829
+ "format": "date-time",
830
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
831
+ },
832
+ "createdAt": {
833
+ "type": "string",
834
+ "format": "date-time",
835
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
836
+ }
837
+ },
838
+ "required": [
839
+ "id",
840
+ "userId",
841
+ "tenantId",
842
+ "type",
843
+ "payload",
844
+ "readAt",
845
+ "createdAt"
846
+ ],
847
+ "additionalProperties": false
848
+ }
849
+ },
850
+ "nextCursor": {
851
+ "nullable": true,
852
+ "type": "string",
853
+ "format": "uuid",
854
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
855
+ }
856
+ },
857
+ "required": [
858
+ "items",
859
+ "nextCursor"
860
+ ],
861
+ "additionalProperties": false
862
+ }
863
+ }
864
+ }
865
+ }
866
+ }
867
+ }
868
+ },
869
+ "/notifications/{id}/read": {
870
+ "patch": {
871
+ "operationId": "markNotificationRead",
872
+ "summary": "Mark a notification as read",
873
+ "tags": [
874
+ "notifications"
875
+ ],
876
+ "parameters": [
877
+ {
878
+ "schema": {
879
+ "type": "string",
880
+ "format": "uuid",
881
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
882
+ },
883
+ "in": "path",
884
+ "name": "id",
885
+ "required": true
886
+ }
887
+ ],
888
+ "security": [
889
+ {
890
+ "bearerAuth": []
891
+ }
892
+ ],
893
+ "responses": {
894
+ "200": {
895
+ "description": "Default Response",
896
+ "content": {
897
+ "application/json": {
898
+ "schema": {
899
+ "type": "object",
900
+ "properties": {
901
+ "id": {
902
+ "type": "string",
903
+ "format": "uuid",
904
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
905
+ },
906
+ "userId": {
907
+ "type": "string",
908
+ "format": "uuid",
909
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
910
+ },
911
+ "tenantId": {
912
+ "type": "integer",
913
+ "minimum": -9007199254740991,
914
+ "maximum": 9007199254740991
915
+ },
916
+ "type": {
917
+ "type": "string",
918
+ "enum": [
919
+ "new_post"
920
+ ]
921
+ },
922
+ "payload": {
923
+ "type": "object",
924
+ "additionalProperties": {}
925
+ },
926
+ "readAt": {
927
+ "nullable": true,
928
+ "type": "string",
929
+ "format": "date-time",
930
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
931
+ },
932
+ "createdAt": {
933
+ "type": "string",
934
+ "format": "date-time",
935
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
936
+ }
937
+ },
938
+ "required": [
939
+ "id",
940
+ "userId",
941
+ "tenantId",
942
+ "type",
943
+ "payload",
944
+ "readAt",
945
+ "createdAt"
946
+ ],
947
+ "additionalProperties": false
948
+ }
949
+ }
950
+ }
951
+ }
952
+ }
953
+ }
954
+ },
955
+ "/notifications/read-all": {
956
+ "post": {
957
+ "summary": "Mark all notifications as read",
958
+ "tags": [
959
+ "notifications"
960
+ ],
961
+ "security": [
962
+ {
963
+ "bearerAuth": []
964
+ }
965
+ ],
966
+ "responses": {
967
+ "204": {
968
+ "description": "Default Response",
969
+ "content": {
970
+ "application/json": {
971
+ "schema": {
972
+ "type": "string",
973
+ "nullable": true,
974
+ "enum": [
975
+ null
976
+ ]
977
+ }
978
+ }
979
+ }
980
+ }
981
+ }
982
+ }
983
+ },
984
+ "/notifications/{id}": {
985
+ "delete": {
986
+ "operationId": "deleteNotification",
987
+ "summary": "Delete a notification",
988
+ "tags": [
989
+ "notifications"
990
+ ],
991
+ "parameters": [
992
+ {
993
+ "schema": {
994
+ "type": "string",
995
+ "format": "uuid",
996
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
997
+ },
998
+ "in": "path",
999
+ "name": "id",
1000
+ "required": true
1001
+ }
1002
+ ],
1003
+ "security": [
1004
+ {
1005
+ "bearerAuth": []
1006
+ }
1007
+ ],
1008
+ "responses": {
1009
+ "204": {
1010
+ "description": "Default Response",
1011
+ "content": {
1012
+ "application/json": {
1013
+ "schema": {
1014
+ "type": "string",
1015
+ "nullable": true,
1016
+ "enum": [
1017
+ null
1018
+ ]
1019
+ }
1020
+ }
1021
+ }
1022
+ }
1023
+ }
1024
+ }
1025
+ },
1026
+ "/categories": {
1027
+ "get": {
1028
+ "operationId": "listCategories",
1029
+ "summary": "List visible categories for the requesting user",
1030
+ "tags": [
1031
+ "categories"
1032
+ ],
1033
+ "responses": {
1034
+ "200": {
1035
+ "description": "Default Response",
1036
+ "content": {
1037
+ "application/json": {
1038
+ "schema": {
1039
+ "type": "array",
1040
+ "items": {
1041
+ "type": "object",
1042
+ "properties": {
1043
+ "id": {
1044
+ "type": "string",
1045
+ "format": "uuid",
1046
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1047
+ },
1048
+ "name": {
1049
+ "type": "string"
1050
+ },
1051
+ "description": {
1052
+ "nullable": true,
1053
+ "type": "string"
1054
+ },
1055
+ "position": {
1056
+ "type": "integer",
1057
+ "minimum": -9007199254740991,
1058
+ "maximum": 9007199254740991
1059
+ },
1060
+ "threadCount": {
1061
+ "type": "integer",
1062
+ "minimum": -9007199254740991,
1063
+ "maximum": 9007199254740991
1064
+ },
1065
+ "createdAt": {
1066
+ "type": "string",
1067
+ "format": "date-time",
1068
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1069
+ }
1070
+ },
1071
+ "required": [
1072
+ "id",
1073
+ "name",
1074
+ "description",
1075
+ "position",
1076
+ "threadCount",
1077
+ "createdAt"
1078
+ ],
1079
+ "additionalProperties": false
1080
+ }
1081
+ }
1082
+ }
1083
+ }
1084
+ }
1085
+ }
1086
+ },
1087
+ "post": {
1088
+ "operationId": "createCategory",
1089
+ "summary": "Create a category (admin)",
1090
+ "tags": [
1091
+ "categories"
1092
+ ],
1093
+ "requestBody": {
1094
+ "required": true,
1095
+ "content": {
1096
+ "application/json": {
1097
+ "schema": {
1098
+ "type": "object",
1099
+ "properties": {
1100
+ "name": {
1101
+ "type": "string",
1102
+ "minLength": 1,
1103
+ "maxLength": 200
1104
+ },
1105
+ "description": {
1106
+ "nullable": true,
1107
+ "type": "string",
1108
+ "maxLength": 1000
1109
+ },
1110
+ "position": {
1111
+ "type": "integer",
1112
+ "minimum": 0,
1113
+ "maximum": 9007199254740991
1114
+ }
1115
+ },
1116
+ "required": [
1117
+ "name"
1118
+ ]
1119
+ }
1120
+ }
1121
+ }
1122
+ },
1123
+ "security": [
1124
+ {
1125
+ "bearerAuth": []
1126
+ }
1127
+ ],
1128
+ "responses": {
1129
+ "201": {
1130
+ "description": "Default Response",
1131
+ "content": {
1132
+ "application/json": {
1133
+ "schema": {
1134
+ "type": "object",
1135
+ "properties": {
1136
+ "id": {
1137
+ "type": "string",
1138
+ "format": "uuid",
1139
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1140
+ },
1141
+ "name": {
1142
+ "type": "string"
1143
+ },
1144
+ "description": {
1145
+ "nullable": true,
1146
+ "type": "string"
1147
+ },
1148
+ "position": {
1149
+ "type": "integer",
1150
+ "minimum": -9007199254740991,
1151
+ "maximum": 9007199254740991
1152
+ },
1153
+ "threadCount": {
1154
+ "type": "integer",
1155
+ "minimum": -9007199254740991,
1156
+ "maximum": 9007199254740991
1157
+ },
1158
+ "createdAt": {
1159
+ "type": "string",
1160
+ "format": "date-time",
1161
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1162
+ }
1163
+ },
1164
+ "required": [
1165
+ "id",
1166
+ "name",
1167
+ "description",
1168
+ "position",
1169
+ "threadCount",
1170
+ "createdAt"
1171
+ ],
1172
+ "additionalProperties": false
1173
+ }
1174
+ }
1175
+ }
1176
+ }
1177
+ }
1178
+ }
1179
+ },
1180
+ "/categories/{id}": {
1181
+ "get": {
1182
+ "operationId": "getCategory",
1183
+ "summary": "Get a single category",
1184
+ "tags": [
1185
+ "categories"
1186
+ ],
1187
+ "parameters": [
1188
+ {
1189
+ "schema": {
1190
+ "type": "string",
1191
+ "format": "uuid",
1192
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1193
+ },
1194
+ "in": "path",
1195
+ "name": "id",
1196
+ "required": true
1197
+ }
1198
+ ],
1199
+ "responses": {
1200
+ "200": {
1201
+ "description": "Default Response",
1202
+ "content": {
1203
+ "application/json": {
1204
+ "schema": {
1205
+ "type": "object",
1206
+ "properties": {
1207
+ "id": {
1208
+ "type": "string",
1209
+ "format": "uuid",
1210
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1211
+ },
1212
+ "name": {
1213
+ "type": "string"
1214
+ },
1215
+ "description": {
1216
+ "nullable": true,
1217
+ "type": "string"
1218
+ },
1219
+ "position": {
1220
+ "type": "integer",
1221
+ "minimum": -9007199254740991,
1222
+ "maximum": 9007199254740991
1223
+ },
1224
+ "threadCount": {
1225
+ "type": "integer",
1226
+ "minimum": -9007199254740991,
1227
+ "maximum": 9007199254740991
1228
+ },
1229
+ "createdAt": {
1230
+ "type": "string",
1231
+ "format": "date-time",
1232
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1233
+ }
1234
+ },
1235
+ "required": [
1236
+ "id",
1237
+ "name",
1238
+ "description",
1239
+ "position",
1240
+ "threadCount",
1241
+ "createdAt"
1242
+ ],
1243
+ "additionalProperties": false
1244
+ }
1245
+ }
1246
+ }
1247
+ }
1248
+ }
1249
+ },
1250
+ "patch": {
1251
+ "operationId": "updateCategory",
1252
+ "summary": "Update a category (admin)",
1253
+ "tags": [
1254
+ "categories"
1255
+ ],
1256
+ "requestBody": {
1257
+ "required": true,
1258
+ "content": {
1259
+ "application/json": {
1260
+ "schema": {
1261
+ "type": "object",
1262
+ "properties": {
1263
+ "name": {
1264
+ "type": "string",
1265
+ "minLength": 1,
1266
+ "maxLength": 200
1267
+ },
1268
+ "description": {
1269
+ "nullable": true,
1270
+ "type": "string",
1271
+ "maxLength": 1000
1272
+ },
1273
+ "position": {
1274
+ "type": "integer",
1275
+ "minimum": 0,
1276
+ "maximum": 9007199254740991
1277
+ }
1278
+ }
1279
+ }
1280
+ }
1281
+ }
1282
+ },
1283
+ "parameters": [
1284
+ {
1285
+ "schema": {
1286
+ "type": "string",
1287
+ "format": "uuid",
1288
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1289
+ },
1290
+ "in": "path",
1291
+ "name": "id",
1292
+ "required": true
1293
+ }
1294
+ ],
1295
+ "security": [
1296
+ {
1297
+ "bearerAuth": []
1298
+ }
1299
+ ],
1300
+ "responses": {
1301
+ "200": {
1302
+ "description": "Default Response",
1303
+ "content": {
1304
+ "application/json": {
1305
+ "schema": {
1306
+ "type": "object",
1307
+ "properties": {
1308
+ "id": {
1309
+ "type": "string",
1310
+ "format": "uuid",
1311
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1312
+ },
1313
+ "name": {
1314
+ "type": "string"
1315
+ },
1316
+ "description": {
1317
+ "nullable": true,
1318
+ "type": "string"
1319
+ },
1320
+ "position": {
1321
+ "type": "integer",
1322
+ "minimum": -9007199254740991,
1323
+ "maximum": 9007199254740991
1324
+ },
1325
+ "threadCount": {
1326
+ "type": "integer",
1327
+ "minimum": -9007199254740991,
1328
+ "maximum": 9007199254740991
1329
+ },
1330
+ "createdAt": {
1331
+ "type": "string",
1332
+ "format": "date-time",
1333
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1334
+ }
1335
+ },
1336
+ "required": [
1337
+ "id",
1338
+ "name",
1339
+ "description",
1340
+ "position",
1341
+ "threadCount",
1342
+ "createdAt"
1343
+ ],
1344
+ "additionalProperties": false
1345
+ }
1346
+ }
1347
+ }
1348
+ }
1349
+ }
1350
+ },
1351
+ "delete": {
1352
+ "operationId": "deleteCategory",
1353
+ "summary": "Delete a category (admin)",
1354
+ "tags": [
1355
+ "categories"
1356
+ ],
1357
+ "parameters": [
1358
+ {
1359
+ "schema": {
1360
+ "type": "string",
1361
+ "format": "uuid",
1362
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1363
+ },
1364
+ "in": "path",
1365
+ "name": "id",
1366
+ "required": true
1367
+ }
1368
+ ],
1369
+ "security": [
1370
+ {
1371
+ "bearerAuth": []
1372
+ }
1373
+ ],
1374
+ "responses": {
1375
+ "204": {
1376
+ "description": "Default Response",
1377
+ "content": {
1378
+ "application/json": {
1379
+ "schema": {
1380
+ "type": "string",
1381
+ "nullable": true,
1382
+ "enum": [
1383
+ null
1384
+ ]
1385
+ }
1386
+ }
1387
+ }
1388
+ }
1389
+ }
1390
+ }
1391
+ },
1392
+ "/categories/{categoryId}/threads": {
1393
+ "get": {
1394
+ "operationId": "listThreads",
1395
+ "summary": "List threads in a category",
1396
+ "tags": [
1397
+ "threads"
1398
+ ],
1399
+ "parameters": [
1400
+ {
1401
+ "schema": {
1402
+ "type": "string",
1403
+ "format": "uuid",
1404
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1405
+ },
1406
+ "in": "query",
1407
+ "name": "cursor",
1408
+ "required": false
1409
+ },
1410
+ {
1411
+ "schema": {
1412
+ "default": 30,
1413
+ "type": "integer",
1414
+ "minimum": 1,
1415
+ "maximum": 100
1416
+ },
1417
+ "in": "query",
1418
+ "name": "limit",
1419
+ "required": false
1420
+ },
1421
+ {
1422
+ "schema": {
1423
+ "type": "string",
1424
+ "format": "uuid",
1425
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1426
+ },
1427
+ "in": "path",
1428
+ "name": "categoryId",
1429
+ "required": true
1430
+ }
1431
+ ],
1432
+ "responses": {
1433
+ "200": {
1434
+ "description": "Default Response",
1435
+ "content": {
1436
+ "application/json": {
1437
+ "schema": {
1438
+ "type": "object",
1439
+ "properties": {
1440
+ "items": {
1441
+ "type": "array",
1442
+ "items": {
1443
+ "type": "object",
1444
+ "properties": {
1445
+ "id": {
1446
+ "type": "string",
1447
+ "format": "uuid",
1448
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1449
+ },
1450
+ "categoryId": {
1451
+ "type": "string",
1452
+ "format": "uuid",
1453
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1454
+ },
1455
+ "title": {
1456
+ "type": "string"
1457
+ },
1458
+ "authorId": {
1459
+ "type": "string",
1460
+ "format": "uuid",
1461
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1462
+ },
1463
+ "author": {
1464
+ "type": "object",
1465
+ "properties": {
1466
+ "userId": {
1467
+ "type": "string",
1468
+ "format": "uuid",
1469
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1470
+ },
1471
+ "displayName": {
1472
+ "type": "string"
1473
+ },
1474
+ "avatarUrl": {
1475
+ "nullable": true,
1476
+ "type": "string",
1477
+ "format": "uri"
1478
+ },
1479
+ "bio": {
1480
+ "nullable": true,
1481
+ "type": "string"
1482
+ },
1483
+ "updatedAt": {
1484
+ "type": "string",
1485
+ "format": "date-time",
1486
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1487
+ }
1488
+ },
1489
+ "required": [
1490
+ "userId",
1491
+ "displayName",
1492
+ "avatarUrl",
1493
+ "bio",
1494
+ "updatedAt"
1495
+ ],
1496
+ "additionalProperties": false
1497
+ },
1498
+ "postCount": {
1499
+ "type": "integer",
1500
+ "minimum": -9007199254740991,
1501
+ "maximum": 9007199254740991
1502
+ },
1503
+ "isPinned": {
1504
+ "type": "boolean"
1505
+ },
1506
+ "isLocked": {
1507
+ "type": "boolean"
1508
+ },
1509
+ "lastPostAt": {
1510
+ "nullable": true,
1511
+ "type": "string",
1512
+ "format": "date-time",
1513
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1514
+ },
1515
+ "createdAt": {
1516
+ "type": "string",
1517
+ "format": "date-time",
1518
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1519
+ }
1520
+ },
1521
+ "required": [
1522
+ "id",
1523
+ "categoryId",
1524
+ "title",
1525
+ "authorId",
1526
+ "author",
1527
+ "postCount",
1528
+ "isPinned",
1529
+ "isLocked",
1530
+ "lastPostAt",
1531
+ "createdAt"
1532
+ ],
1533
+ "additionalProperties": false
1534
+ }
1535
+ },
1536
+ "nextCursor": {
1537
+ "nullable": true,
1538
+ "type": "string",
1539
+ "format": "uuid",
1540
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1541
+ }
1542
+ },
1543
+ "required": [
1544
+ "items",
1545
+ "nextCursor"
1546
+ ],
1547
+ "additionalProperties": false
1548
+ }
1549
+ }
1550
+ }
1551
+ }
1552
+ }
1553
+ },
1554
+ "post": {
1555
+ "operationId": "createThread",
1556
+ "summary": "Create a thread (and its first post)",
1557
+ "tags": [
1558
+ "threads"
1559
+ ],
1560
+ "requestBody": {
1561
+ "required": true,
1562
+ "content": {
1563
+ "application/json": {
1564
+ "schema": {
1565
+ "type": "object",
1566
+ "properties": {
1567
+ "title": {
1568
+ "type": "string",
1569
+ "minLength": 1,
1570
+ "maxLength": 500
1571
+ },
1572
+ "content": {
1573
+ "type": "string",
1574
+ "minLength": 1
1575
+ }
1576
+ },
1577
+ "required": [
1578
+ "title",
1579
+ "content"
1580
+ ]
1581
+ }
1582
+ }
1583
+ }
1584
+ },
1585
+ "parameters": [
1586
+ {
1587
+ "schema": {
1588
+ "type": "string",
1589
+ "format": "uuid",
1590
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1591
+ },
1592
+ "in": "path",
1593
+ "name": "categoryId",
1594
+ "required": true
1595
+ }
1596
+ ],
1597
+ "security": [
1598
+ {
1599
+ "bearerAuth": []
1600
+ }
1601
+ ],
1602
+ "responses": {
1603
+ "201": {
1604
+ "description": "Default Response",
1605
+ "content": {
1606
+ "application/json": {
1607
+ "schema": {
1608
+ "type": "object",
1609
+ "properties": {
1610
+ "id": {
1611
+ "type": "string",
1612
+ "format": "uuid",
1613
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1614
+ },
1615
+ "categoryId": {
1616
+ "type": "string",
1617
+ "format": "uuid",
1618
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1619
+ },
1620
+ "title": {
1621
+ "type": "string"
1622
+ },
1623
+ "authorId": {
1624
+ "type": "string",
1625
+ "format": "uuid",
1626
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1627
+ },
1628
+ "author": {
1629
+ "type": "object",
1630
+ "properties": {
1631
+ "userId": {
1632
+ "type": "string",
1633
+ "format": "uuid",
1634
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1635
+ },
1636
+ "displayName": {
1637
+ "type": "string"
1638
+ },
1639
+ "avatarUrl": {
1640
+ "nullable": true,
1641
+ "type": "string",
1642
+ "format": "uri"
1643
+ },
1644
+ "bio": {
1645
+ "nullable": true,
1646
+ "type": "string"
1647
+ },
1648
+ "updatedAt": {
1649
+ "type": "string",
1650
+ "format": "date-time",
1651
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1652
+ }
1653
+ },
1654
+ "required": [
1655
+ "userId",
1656
+ "displayName",
1657
+ "avatarUrl",
1658
+ "bio",
1659
+ "updatedAt"
1660
+ ],
1661
+ "additionalProperties": false
1662
+ },
1663
+ "postCount": {
1664
+ "type": "integer",
1665
+ "minimum": -9007199254740991,
1666
+ "maximum": 9007199254740991
1667
+ },
1668
+ "isPinned": {
1669
+ "type": "boolean"
1670
+ },
1671
+ "isLocked": {
1672
+ "type": "boolean"
1673
+ },
1674
+ "lastPostAt": {
1675
+ "nullable": true,
1676
+ "type": "string",
1677
+ "format": "date-time",
1678
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1679
+ },
1680
+ "createdAt": {
1681
+ "type": "string",
1682
+ "format": "date-time",
1683
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1684
+ }
1685
+ },
1686
+ "required": [
1687
+ "id",
1688
+ "categoryId",
1689
+ "title",
1690
+ "authorId",
1691
+ "author",
1692
+ "postCount",
1693
+ "isPinned",
1694
+ "isLocked",
1695
+ "lastPostAt",
1696
+ "createdAt"
1697
+ ],
1698
+ "additionalProperties": false
1699
+ }
1700
+ }
1701
+ }
1702
+ }
1703
+ }
1704
+ }
1705
+ },
1706
+ "/threads/{id}": {
1707
+ "get": {
1708
+ "operationId": "getThread",
1709
+ "summary": "Get a single thread",
1710
+ "tags": [
1711
+ "threads"
1712
+ ],
1713
+ "parameters": [
1714
+ {
1715
+ "schema": {
1716
+ "type": "string",
1717
+ "format": "uuid",
1718
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1719
+ },
1720
+ "in": "path",
1721
+ "name": "id",
1722
+ "required": true
1723
+ }
1724
+ ],
1725
+ "responses": {
1726
+ "200": {
1727
+ "description": "Default Response",
1728
+ "content": {
1729
+ "application/json": {
1730
+ "schema": {
1731
+ "type": "object",
1732
+ "properties": {
1733
+ "id": {
1734
+ "type": "string",
1735
+ "format": "uuid",
1736
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1737
+ },
1738
+ "categoryId": {
1739
+ "type": "string",
1740
+ "format": "uuid",
1741
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1742
+ },
1743
+ "title": {
1744
+ "type": "string"
1745
+ },
1746
+ "authorId": {
1747
+ "type": "string",
1748
+ "format": "uuid",
1749
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1750
+ },
1751
+ "author": {
1752
+ "type": "object",
1753
+ "properties": {
1754
+ "userId": {
1755
+ "type": "string",
1756
+ "format": "uuid",
1757
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1758
+ },
1759
+ "displayName": {
1760
+ "type": "string"
1761
+ },
1762
+ "avatarUrl": {
1763
+ "nullable": true,
1764
+ "type": "string",
1765
+ "format": "uri"
1766
+ },
1767
+ "bio": {
1768
+ "nullable": true,
1769
+ "type": "string"
1770
+ },
1771
+ "updatedAt": {
1772
+ "type": "string",
1773
+ "format": "date-time",
1774
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1775
+ }
1776
+ },
1777
+ "required": [
1778
+ "userId",
1779
+ "displayName",
1780
+ "avatarUrl",
1781
+ "bio",
1782
+ "updatedAt"
1783
+ ],
1784
+ "additionalProperties": false
1785
+ },
1786
+ "postCount": {
1787
+ "type": "integer",
1788
+ "minimum": -9007199254740991,
1789
+ "maximum": 9007199254740991
1790
+ },
1791
+ "isPinned": {
1792
+ "type": "boolean"
1793
+ },
1794
+ "isLocked": {
1795
+ "type": "boolean"
1796
+ },
1797
+ "lastPostAt": {
1798
+ "nullable": true,
1799
+ "type": "string",
1800
+ "format": "date-time",
1801
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1802
+ },
1803
+ "createdAt": {
1804
+ "type": "string",
1805
+ "format": "date-time",
1806
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1807
+ }
1808
+ },
1809
+ "required": [
1810
+ "id",
1811
+ "categoryId",
1812
+ "title",
1813
+ "authorId",
1814
+ "author",
1815
+ "postCount",
1816
+ "isPinned",
1817
+ "isLocked",
1818
+ "lastPostAt",
1819
+ "createdAt"
1820
+ ],
1821
+ "additionalProperties": false
1822
+ }
1823
+ }
1824
+ }
1825
+ }
1826
+ }
1827
+ },
1828
+ "patch": {
1829
+ "operationId": "updateThread",
1830
+ "summary": "Update a thread (title, pin, lock, move)",
1831
+ "tags": [
1832
+ "threads"
1833
+ ],
1834
+ "requestBody": {
1835
+ "required": true,
1836
+ "content": {
1837
+ "application/json": {
1838
+ "schema": {
1839
+ "type": "object",
1840
+ "properties": {
1841
+ "title": {
1842
+ "type": "string",
1843
+ "minLength": 1,
1844
+ "maxLength": 500
1845
+ },
1846
+ "isPinned": {
1847
+ "type": "boolean"
1848
+ },
1849
+ "isLocked": {
1850
+ "type": "boolean"
1851
+ },
1852
+ "categoryId": {
1853
+ "type": "string",
1854
+ "format": "uuid",
1855
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1856
+ }
1857
+ }
1858
+ }
1859
+ }
1860
+ }
1861
+ },
1862
+ "parameters": [
1863
+ {
1864
+ "schema": {
1865
+ "type": "string",
1866
+ "format": "uuid",
1867
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1868
+ },
1869
+ "in": "path",
1870
+ "name": "id",
1871
+ "required": true
1872
+ }
1873
+ ],
1874
+ "security": [
1875
+ {
1876
+ "bearerAuth": []
1877
+ }
1878
+ ],
1879
+ "responses": {
1880
+ "200": {
1881
+ "description": "Default Response",
1882
+ "content": {
1883
+ "application/json": {
1884
+ "schema": {
1885
+ "type": "object",
1886
+ "properties": {
1887
+ "id": {
1888
+ "type": "string",
1889
+ "format": "uuid",
1890
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1891
+ },
1892
+ "categoryId": {
1893
+ "type": "string",
1894
+ "format": "uuid",
1895
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1896
+ },
1897
+ "title": {
1898
+ "type": "string"
1899
+ },
1900
+ "authorId": {
1901
+ "type": "string",
1902
+ "format": "uuid",
1903
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1904
+ },
1905
+ "author": {
1906
+ "type": "object",
1907
+ "properties": {
1908
+ "userId": {
1909
+ "type": "string",
1910
+ "format": "uuid",
1911
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1912
+ },
1913
+ "displayName": {
1914
+ "type": "string"
1915
+ },
1916
+ "avatarUrl": {
1917
+ "nullable": true,
1918
+ "type": "string",
1919
+ "format": "uri"
1920
+ },
1921
+ "bio": {
1922
+ "nullable": true,
1923
+ "type": "string"
1924
+ },
1925
+ "updatedAt": {
1926
+ "type": "string",
1927
+ "format": "date-time",
1928
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1929
+ }
1930
+ },
1931
+ "required": [
1932
+ "userId",
1933
+ "displayName",
1934
+ "avatarUrl",
1935
+ "bio",
1936
+ "updatedAt"
1937
+ ],
1938
+ "additionalProperties": false
1939
+ },
1940
+ "postCount": {
1941
+ "type": "integer",
1942
+ "minimum": -9007199254740991,
1943
+ "maximum": 9007199254740991
1944
+ },
1945
+ "isPinned": {
1946
+ "type": "boolean"
1947
+ },
1948
+ "isLocked": {
1949
+ "type": "boolean"
1950
+ },
1951
+ "lastPostAt": {
1952
+ "nullable": true,
1953
+ "type": "string",
1954
+ "format": "date-time",
1955
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1956
+ },
1957
+ "createdAt": {
1958
+ "type": "string",
1959
+ "format": "date-time",
1960
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
1961
+ }
1962
+ },
1963
+ "required": [
1964
+ "id",
1965
+ "categoryId",
1966
+ "title",
1967
+ "authorId",
1968
+ "author",
1969
+ "postCount",
1970
+ "isPinned",
1971
+ "isLocked",
1972
+ "lastPostAt",
1973
+ "createdAt"
1974
+ ],
1975
+ "additionalProperties": false
1976
+ }
1977
+ }
1978
+ }
1979
+ }
1980
+ }
1981
+ },
1982
+ "delete": {
1983
+ "operationId": "deleteThread",
1984
+ "summary": "Delete a thread",
1985
+ "tags": [
1986
+ "threads"
1987
+ ],
1988
+ "parameters": [
1989
+ {
1990
+ "schema": {
1991
+ "type": "string",
1992
+ "format": "uuid",
1993
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
1994
+ },
1995
+ "in": "path",
1996
+ "name": "id",
1997
+ "required": true
1998
+ }
1999
+ ],
2000
+ "security": [
2001
+ {
2002
+ "bearerAuth": []
2003
+ }
2004
+ ],
2005
+ "responses": {
2006
+ "204": {
2007
+ "description": "Default Response",
2008
+ "content": {
2009
+ "application/json": {
2010
+ "schema": {
2011
+ "type": "string",
2012
+ "nullable": true,
2013
+ "enum": [
2014
+ null
2015
+ ]
2016
+ }
2017
+ }
2018
+ }
2019
+ }
2020
+ }
2021
+ }
2022
+ },
2023
+ "/threads/{id}/subscribe": {
2024
+ "post": {
2025
+ "operationId": "subscribeThread",
2026
+ "summary": "Subscribe to a thread",
2027
+ "tags": [
2028
+ "threads"
2029
+ ],
2030
+ "parameters": [
2031
+ {
2032
+ "schema": {
2033
+ "type": "string",
2034
+ "format": "uuid",
2035
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2036
+ },
2037
+ "in": "path",
2038
+ "name": "id",
2039
+ "required": true
2040
+ }
2041
+ ],
2042
+ "security": [
2043
+ {
2044
+ "bearerAuth": []
2045
+ }
2046
+ ],
2047
+ "responses": {
2048
+ "204": {
2049
+ "description": "Default Response",
2050
+ "content": {
2051
+ "application/json": {
2052
+ "schema": {
2053
+ "type": "string",
2054
+ "nullable": true,
2055
+ "enum": [
2056
+ null
2057
+ ]
2058
+ }
2059
+ }
2060
+ }
2061
+ }
2062
+ }
2063
+ },
2064
+ "delete": {
2065
+ "operationId": "unsubscribeThread",
2066
+ "summary": "Unsubscribe from a thread",
2067
+ "tags": [
2068
+ "threads"
2069
+ ],
2070
+ "parameters": [
2071
+ {
2072
+ "schema": {
2073
+ "type": "string",
2074
+ "format": "uuid",
2075
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2076
+ },
2077
+ "in": "path",
2078
+ "name": "id",
2079
+ "required": true
2080
+ }
2081
+ ],
2082
+ "security": [
2083
+ {
2084
+ "bearerAuth": []
2085
+ }
2086
+ ],
2087
+ "responses": {
2088
+ "204": {
2089
+ "description": "Default Response",
2090
+ "content": {
2091
+ "application/json": {
2092
+ "schema": {
2093
+ "type": "string",
2094
+ "nullable": true,
2095
+ "enum": [
2096
+ null
2097
+ ]
2098
+ }
2099
+ }
2100
+ }
2101
+ }
2102
+ }
2103
+ }
2104
+ },
2105
+ "/threads/{id}/read-status": {
2106
+ "get": {
2107
+ "operationId": "getThreadReadStatus",
2108
+ "summary": "Get read status for the current user in a thread",
2109
+ "tags": [
2110
+ "threads"
2111
+ ],
2112
+ "parameters": [
2113
+ {
2114
+ "schema": {
2115
+ "type": "string",
2116
+ "format": "uuid",
2117
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2118
+ },
2119
+ "in": "path",
2120
+ "name": "id",
2121
+ "required": true
2122
+ }
2123
+ ],
2124
+ "security": [
2125
+ {
2126
+ "bearerAuth": []
2127
+ }
2128
+ ],
2129
+ "responses": {
2130
+ "200": {
2131
+ "description": "Default Response",
2132
+ "content": {
2133
+ "application/json": {
2134
+ "schema": {
2135
+ "type": "object",
2136
+ "properties": {
2137
+ "lastReadPostId": {
2138
+ "nullable": true,
2139
+ "type": "string",
2140
+ "format": "uuid",
2141
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2142
+ }
2143
+ },
2144
+ "required": [
2145
+ "lastReadPostId"
2146
+ ],
2147
+ "additionalProperties": false
2148
+ }
2149
+ }
2150
+ }
2151
+ }
2152
+ }
2153
+ },
2154
+ "put": {
2155
+ "operationId": "setThreadReadStatus",
2156
+ "summary": "Mark a thread as read up to a given post",
2157
+ "tags": [
2158
+ "threads"
2159
+ ],
2160
+ "requestBody": {
2161
+ "required": true,
2162
+ "content": {
2163
+ "application/json": {
2164
+ "schema": {
2165
+ "type": "object",
2166
+ "properties": {
2167
+ "lastReadPostId": {
2168
+ "type": "string",
2169
+ "format": "uuid",
2170
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2171
+ }
2172
+ },
2173
+ "required": [
2174
+ "lastReadPostId"
2175
+ ]
2176
+ }
2177
+ }
2178
+ }
2179
+ },
2180
+ "parameters": [
2181
+ {
2182
+ "schema": {
2183
+ "type": "string",
2184
+ "format": "uuid",
2185
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2186
+ },
2187
+ "in": "path",
2188
+ "name": "id",
2189
+ "required": true
2190
+ }
2191
+ ],
2192
+ "security": [
2193
+ {
2194
+ "bearerAuth": []
2195
+ }
2196
+ ],
2197
+ "responses": {
2198
+ "204": {
2199
+ "description": "Default Response",
2200
+ "content": {
2201
+ "application/json": {
2202
+ "schema": {
2203
+ "type": "string",
2204
+ "nullable": true,
2205
+ "enum": [
2206
+ null
2207
+ ]
2208
+ }
2209
+ }
2210
+ }
2211
+ }
2212
+ }
2213
+ }
2214
+ },
2215
+ "/threads/{threadId}/posts": {
2216
+ "get": {
2217
+ "operationId": "listPosts",
2218
+ "summary": "List posts in a thread — cursor pagination or anchor via ?from=postId",
2219
+ "tags": [
2220
+ "posts"
2221
+ ],
2222
+ "parameters": [
2223
+ {
2224
+ "schema": {
2225
+ "type": "string",
2226
+ "format": "uuid",
2227
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2228
+ },
2229
+ "in": "query",
2230
+ "name": "cursor",
2231
+ "required": false
2232
+ },
2233
+ {
2234
+ "schema": {
2235
+ "default": 30,
2236
+ "type": "integer",
2237
+ "minimum": 1,
2238
+ "maximum": 100
2239
+ },
2240
+ "in": "query",
2241
+ "name": "limit",
2242
+ "required": false
2243
+ },
2244
+ {
2245
+ "schema": {
2246
+ "type": "string",
2247
+ "format": "uuid",
2248
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2249
+ },
2250
+ "in": "query",
2251
+ "name": "from",
2252
+ "required": false
2253
+ },
2254
+ {
2255
+ "schema": {
2256
+ "type": "string",
2257
+ "format": "uuid",
2258
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2259
+ },
2260
+ "in": "path",
2261
+ "name": "threadId",
2262
+ "required": true
2263
+ }
2264
+ ],
2265
+ "responses": {
2266
+ "200": {
2267
+ "description": "Default Response",
2268
+ "content": {
2269
+ "application/json": {
2270
+ "schema": {
2271
+ "type": "object",
2272
+ "properties": {
2273
+ "items": {
2274
+ "type": "array",
2275
+ "items": {
2276
+ "type": "object",
2277
+ "properties": {
2278
+ "id": {
2279
+ "type": "string",
2280
+ "format": "uuid",
2281
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2282
+ },
2283
+ "threadId": {
2284
+ "type": "string",
2285
+ "format": "uuid",
2286
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2287
+ },
2288
+ "authorId": {
2289
+ "type": "string",
2290
+ "format": "uuid",
2291
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2292
+ },
2293
+ "author": {
2294
+ "type": "object",
2295
+ "properties": {
2296
+ "userId": {
2297
+ "type": "string",
2298
+ "format": "uuid",
2299
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2300
+ },
2301
+ "displayName": {
2302
+ "type": "string"
2303
+ },
2304
+ "avatarUrl": {
2305
+ "nullable": true,
2306
+ "type": "string",
2307
+ "format": "uri"
2308
+ },
2309
+ "bio": {
2310
+ "nullable": true,
2311
+ "type": "string"
2312
+ },
2313
+ "updatedAt": {
2314
+ "type": "string",
2315
+ "format": "date-time",
2316
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2317
+ }
2318
+ },
2319
+ "required": [
2320
+ "userId",
2321
+ "displayName",
2322
+ "avatarUrl",
2323
+ "bio",
2324
+ "updatedAt"
2325
+ ],
2326
+ "additionalProperties": false
2327
+ },
2328
+ "content": {
2329
+ "nullable": true,
2330
+ "type": "string"
2331
+ },
2332
+ "deletedAt": {
2333
+ "nullable": true,
2334
+ "type": "string",
2335
+ "format": "date-time",
2336
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2337
+ },
2338
+ "createdAt": {
2339
+ "type": "string",
2340
+ "format": "date-time",
2341
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2342
+ },
2343
+ "updatedAt": {
2344
+ "type": "string",
2345
+ "format": "date-time",
2346
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2347
+ }
2348
+ },
2349
+ "required": [
2350
+ "id",
2351
+ "threadId",
2352
+ "authorId",
2353
+ "author",
2354
+ "content",
2355
+ "deletedAt",
2356
+ "createdAt",
2357
+ "updatedAt"
2358
+ ],
2359
+ "additionalProperties": false
2360
+ }
2361
+ },
2362
+ "nextCursor": {
2363
+ "nullable": true,
2364
+ "type": "string",
2365
+ "format": "uuid",
2366
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2367
+ }
2368
+ },
2369
+ "required": [
2370
+ "items",
2371
+ "nextCursor"
2372
+ ],
2373
+ "additionalProperties": false
2374
+ }
2375
+ }
2376
+ }
2377
+ }
2378
+ }
2379
+ },
2380
+ "post": {
2381
+ "operationId": "createPost",
2382
+ "summary": "Create a post in a thread",
2383
+ "tags": [
2384
+ "posts"
2385
+ ],
2386
+ "requestBody": {
2387
+ "required": true,
2388
+ "content": {
2389
+ "application/json": {
2390
+ "schema": {
2391
+ "type": "object",
2392
+ "properties": {
2393
+ "content": {
2394
+ "type": "string",
2395
+ "minLength": 1
2396
+ }
2397
+ },
2398
+ "required": [
2399
+ "content"
2400
+ ]
2401
+ }
2402
+ }
2403
+ }
2404
+ },
2405
+ "parameters": [
2406
+ {
2407
+ "schema": {
2408
+ "type": "string",
2409
+ "format": "uuid",
2410
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2411
+ },
2412
+ "in": "path",
2413
+ "name": "threadId",
2414
+ "required": true
2415
+ }
2416
+ ],
2417
+ "security": [
2418
+ {
2419
+ "bearerAuth": []
2420
+ }
2421
+ ],
2422
+ "responses": {
2423
+ "201": {
2424
+ "description": "Default Response",
2425
+ "content": {
2426
+ "application/json": {
2427
+ "schema": {
2428
+ "type": "object",
2429
+ "properties": {
2430
+ "id": {
2431
+ "type": "string",
2432
+ "format": "uuid",
2433
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2434
+ },
2435
+ "threadId": {
2436
+ "type": "string",
2437
+ "format": "uuid",
2438
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2439
+ },
2440
+ "authorId": {
2441
+ "type": "string",
2442
+ "format": "uuid",
2443
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2444
+ },
2445
+ "author": {
2446
+ "type": "object",
2447
+ "properties": {
2448
+ "userId": {
2449
+ "type": "string",
2450
+ "format": "uuid",
2451
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2452
+ },
2453
+ "displayName": {
2454
+ "type": "string"
2455
+ },
2456
+ "avatarUrl": {
2457
+ "nullable": true,
2458
+ "type": "string",
2459
+ "format": "uri"
2460
+ },
2461
+ "bio": {
2462
+ "nullable": true,
2463
+ "type": "string"
2464
+ },
2465
+ "updatedAt": {
2466
+ "type": "string",
2467
+ "format": "date-time",
2468
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2469
+ }
2470
+ },
2471
+ "required": [
2472
+ "userId",
2473
+ "displayName",
2474
+ "avatarUrl",
2475
+ "bio",
2476
+ "updatedAt"
2477
+ ],
2478
+ "additionalProperties": false
2479
+ },
2480
+ "content": {
2481
+ "nullable": true,
2482
+ "type": "string"
2483
+ },
2484
+ "deletedAt": {
2485
+ "nullable": true,
2486
+ "type": "string",
2487
+ "format": "date-time",
2488
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2489
+ },
2490
+ "createdAt": {
2491
+ "type": "string",
2492
+ "format": "date-time",
2493
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2494
+ },
2495
+ "updatedAt": {
2496
+ "type": "string",
2497
+ "format": "date-time",
2498
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2499
+ }
2500
+ },
2501
+ "required": [
2502
+ "id",
2503
+ "threadId",
2504
+ "authorId",
2505
+ "author",
2506
+ "content",
2507
+ "deletedAt",
2508
+ "createdAt",
2509
+ "updatedAt"
2510
+ ],
2511
+ "additionalProperties": false
2512
+ }
2513
+ }
2514
+ }
2515
+ }
2516
+ }
2517
+ }
2518
+ },
2519
+ "/posts/{id}": {
2520
+ "patch": {
2521
+ "operationId": "updatePost",
2522
+ "summary": "Edit a post (author or moderator)",
2523
+ "tags": [
2524
+ "posts"
2525
+ ],
2526
+ "requestBody": {
2527
+ "required": true,
2528
+ "content": {
2529
+ "application/json": {
2530
+ "schema": {
2531
+ "type": "object",
2532
+ "properties": {
2533
+ "content": {
2534
+ "type": "string",
2535
+ "minLength": 1
2536
+ }
2537
+ },
2538
+ "required": [
2539
+ "content"
2540
+ ]
2541
+ }
2542
+ }
2543
+ }
2544
+ },
2545
+ "parameters": [
2546
+ {
2547
+ "schema": {
2548
+ "type": "string",
2549
+ "format": "uuid",
2550
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2551
+ },
2552
+ "in": "path",
2553
+ "name": "id",
2554
+ "required": true
2555
+ }
2556
+ ],
2557
+ "security": [
2558
+ {
2559
+ "bearerAuth": []
2560
+ }
2561
+ ],
2562
+ "responses": {
2563
+ "200": {
2564
+ "description": "Default Response",
2565
+ "content": {
2566
+ "application/json": {
2567
+ "schema": {
2568
+ "type": "object",
2569
+ "properties": {
2570
+ "id": {
2571
+ "type": "string",
2572
+ "format": "uuid",
2573
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2574
+ },
2575
+ "threadId": {
2576
+ "type": "string",
2577
+ "format": "uuid",
2578
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2579
+ },
2580
+ "authorId": {
2581
+ "type": "string",
2582
+ "format": "uuid",
2583
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2584
+ },
2585
+ "author": {
2586
+ "type": "object",
2587
+ "properties": {
2588
+ "userId": {
2589
+ "type": "string",
2590
+ "format": "uuid",
2591
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2592
+ },
2593
+ "displayName": {
2594
+ "type": "string"
2595
+ },
2596
+ "avatarUrl": {
2597
+ "nullable": true,
2598
+ "type": "string",
2599
+ "format": "uri"
2600
+ },
2601
+ "bio": {
2602
+ "nullable": true,
2603
+ "type": "string"
2604
+ },
2605
+ "updatedAt": {
2606
+ "type": "string",
2607
+ "format": "date-time",
2608
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2609
+ }
2610
+ },
2611
+ "required": [
2612
+ "userId",
2613
+ "displayName",
2614
+ "avatarUrl",
2615
+ "bio",
2616
+ "updatedAt"
2617
+ ],
2618
+ "additionalProperties": false
2619
+ },
2620
+ "content": {
2621
+ "nullable": true,
2622
+ "type": "string"
2623
+ },
2624
+ "deletedAt": {
2625
+ "nullable": true,
2626
+ "type": "string",
2627
+ "format": "date-time",
2628
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2629
+ },
2630
+ "createdAt": {
2631
+ "type": "string",
2632
+ "format": "date-time",
2633
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2634
+ },
2635
+ "updatedAt": {
2636
+ "type": "string",
2637
+ "format": "date-time",
2638
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2639
+ }
2640
+ },
2641
+ "required": [
2642
+ "id",
2643
+ "threadId",
2644
+ "authorId",
2645
+ "author",
2646
+ "content",
2647
+ "deletedAt",
2648
+ "createdAt",
2649
+ "updatedAt"
2650
+ ],
2651
+ "additionalProperties": false
2652
+ }
2653
+ }
2654
+ }
2655
+ }
2656
+ }
2657
+ },
2658
+ "delete": {
2659
+ "operationId": "deletePost",
2660
+ "summary": "Soft-delete a post",
2661
+ "tags": [
2662
+ "posts"
2663
+ ],
2664
+ "parameters": [
2665
+ {
2666
+ "schema": {
2667
+ "type": "string",
2668
+ "format": "uuid",
2669
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2670
+ },
2671
+ "in": "path",
2672
+ "name": "id",
2673
+ "required": true
2674
+ }
2675
+ ],
2676
+ "security": [
2677
+ {
2678
+ "bearerAuth": []
2679
+ }
2680
+ ],
2681
+ "responses": {
2682
+ "204": {
2683
+ "description": "Default Response",
2684
+ "content": {
2685
+ "application/json": {
2686
+ "schema": {
2687
+ "type": "string",
2688
+ "nullable": true,
2689
+ "enum": [
2690
+ null
2691
+ ]
2692
+ }
2693
+ }
2694
+ }
2695
+ }
2696
+ }
2697
+ }
2698
+ },
2699
+ "/users/me": {
2700
+ "get": {
2701
+ "operationId": "getMe",
2702
+ "summary": "Get own profile",
2703
+ "tags": [
2704
+ "users"
2705
+ ],
2706
+ "security": [
2707
+ {
2708
+ "bearerAuth": []
2709
+ }
2710
+ ],
2711
+ "responses": {
2712
+ "200": {
2713
+ "description": "Default Response",
2714
+ "content": {
2715
+ "application/json": {
2716
+ "schema": {
2717
+ "type": "object",
2718
+ "properties": {
2719
+ "userId": {
2720
+ "type": "string",
2721
+ "format": "uuid",
2722
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2723
+ },
2724
+ "displayName": {
2725
+ "type": "string"
2726
+ },
2727
+ "avatarUrl": {
2728
+ "nullable": true,
2729
+ "type": "string",
2730
+ "format": "uri"
2731
+ },
2732
+ "bio": {
2733
+ "nullable": true,
2734
+ "type": "string"
2735
+ },
2736
+ "updatedAt": {
2737
+ "type": "string",
2738
+ "format": "date-time",
2739
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2740
+ }
2741
+ },
2742
+ "required": [
2743
+ "userId",
2744
+ "displayName",
2745
+ "avatarUrl",
2746
+ "bio",
2747
+ "updatedAt"
2748
+ ],
2749
+ "additionalProperties": false
2750
+ }
2751
+ }
2752
+ }
2753
+ }
2754
+ }
2755
+ },
2756
+ "patch": {
2757
+ "operationId": "updateMe",
2758
+ "summary": "Update own profile",
2759
+ "tags": [
2760
+ "users"
2761
+ ],
2762
+ "requestBody": {
2763
+ "required": true,
2764
+ "content": {
2765
+ "application/json": {
2766
+ "schema": {
2767
+ "type": "object",
2768
+ "properties": {
2769
+ "displayName": {
2770
+ "type": "string",
2771
+ "minLength": 1,
2772
+ "maxLength": 100
2773
+ },
2774
+ "avatarUrl": {
2775
+ "nullable": true,
2776
+ "type": "string",
2777
+ "format": "uri"
2778
+ },
2779
+ "bio": {
2780
+ "nullable": true,
2781
+ "type": "string",
2782
+ "maxLength": 500
2783
+ }
2784
+ }
2785
+ }
2786
+ }
2787
+ }
2788
+ },
2789
+ "security": [
2790
+ {
2791
+ "bearerAuth": []
2792
+ }
2793
+ ],
2794
+ "responses": {
2795
+ "200": {
2796
+ "description": "Default Response",
2797
+ "content": {
2798
+ "application/json": {
2799
+ "schema": {
2800
+ "type": "object",
2801
+ "properties": {
2802
+ "userId": {
2803
+ "type": "string",
2804
+ "format": "uuid",
2805
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2806
+ },
2807
+ "displayName": {
2808
+ "type": "string"
2809
+ },
2810
+ "avatarUrl": {
2811
+ "nullable": true,
2812
+ "type": "string",
2813
+ "format": "uri"
2814
+ },
2815
+ "bio": {
2816
+ "nullable": true,
2817
+ "type": "string"
2818
+ },
2819
+ "updatedAt": {
2820
+ "type": "string",
2821
+ "format": "date-time",
2822
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2823
+ }
2824
+ },
2825
+ "required": [
2826
+ "userId",
2827
+ "displayName",
2828
+ "avatarUrl",
2829
+ "bio",
2830
+ "updatedAt"
2831
+ ],
2832
+ "additionalProperties": false
2833
+ }
2834
+ }
2835
+ }
2836
+ }
2837
+ }
2838
+ }
2839
+ },
2840
+ "/users/{id}": {
2841
+ "get": {
2842
+ "operationId": "getUser",
2843
+ "summary": "Get a user profile by ID",
2844
+ "tags": [
2845
+ "users"
2846
+ ],
2847
+ "parameters": [
2848
+ {
2849
+ "schema": {
2850
+ "type": "string",
2851
+ "format": "uuid",
2852
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2853
+ },
2854
+ "in": "path",
2855
+ "name": "id",
2856
+ "required": true
2857
+ }
2858
+ ],
2859
+ "responses": {
2860
+ "200": {
2861
+ "description": "Default Response",
2862
+ "content": {
2863
+ "application/json": {
2864
+ "schema": {
2865
+ "type": "object",
2866
+ "properties": {
2867
+ "userId": {
2868
+ "type": "string",
2869
+ "format": "uuid",
2870
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2871
+ },
2872
+ "displayName": {
2873
+ "type": "string"
2874
+ },
2875
+ "avatarUrl": {
2876
+ "nullable": true,
2877
+ "type": "string",
2878
+ "format": "uri"
2879
+ },
2880
+ "bio": {
2881
+ "nullable": true,
2882
+ "type": "string"
2883
+ },
2884
+ "updatedAt": {
2885
+ "type": "string",
2886
+ "format": "date-time",
2887
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2888
+ }
2889
+ },
2890
+ "required": [
2891
+ "userId",
2892
+ "displayName",
2893
+ "avatarUrl",
2894
+ "bio",
2895
+ "updatedAt"
2896
+ ],
2897
+ "additionalProperties": false
2898
+ }
2899
+ }
2900
+ }
2901
+ }
2902
+ }
2903
+ }
2904
+ },
2905
+ "/users/{id}/membership": {
2906
+ "get": {
2907
+ "operationId": "getUserMembership",
2908
+ "summary": "Get user membership in the current forum",
2909
+ "tags": [
2910
+ "users"
2911
+ ],
2912
+ "parameters": [
2913
+ {
2914
+ "schema": {
2915
+ "type": "string",
2916
+ "format": "uuid",
2917
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2918
+ },
2919
+ "in": "path",
2920
+ "name": "id",
2921
+ "required": true
2922
+ }
2923
+ ],
2924
+ "security": [
2925
+ {
2926
+ "bearerAuth": []
2927
+ }
2928
+ ],
2929
+ "responses": {
2930
+ "200": {
2931
+ "description": "Default Response",
2932
+ "content": {
2933
+ "application/json": {
2934
+ "schema": {
2935
+ "type": "object",
2936
+ "properties": {
2937
+ "userId": {
2938
+ "type": "string",
2939
+ "format": "uuid",
2940
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2941
+ },
2942
+ "groupId": {
2943
+ "nullable": true,
2944
+ "type": "string",
2945
+ "format": "uuid",
2946
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2947
+ },
2948
+ "groupName": {
2949
+ "nullable": true,
2950
+ "type": "string"
2951
+ },
2952
+ "isOwner": {
2953
+ "type": "boolean"
2954
+ },
2955
+ "joinedAt": {
2956
+ "type": "string",
2957
+ "format": "date-time",
2958
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
2959
+ }
2960
+ },
2961
+ "required": [
2962
+ "userId",
2963
+ "groupId",
2964
+ "groupName",
2965
+ "isOwner",
2966
+ "joinedAt"
2967
+ ],
2968
+ "additionalProperties": false
2969
+ }
2970
+ }
2971
+ }
2972
+ }
2973
+ }
2974
+ },
2975
+ "put": {
2976
+ "operationId": "setUserMembership",
2977
+ "summary": "Set user group in the current forum (admin)",
2978
+ "tags": [
2979
+ "users"
2980
+ ],
2981
+ "requestBody": {
2982
+ "required": true,
2983
+ "content": {
2984
+ "application/json": {
2985
+ "schema": {
2986
+ "type": "object",
2987
+ "properties": {
2988
+ "groupId": {
2989
+ "nullable": true,
2990
+ "type": "string",
2991
+ "format": "uuid",
2992
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
2993
+ }
2994
+ },
2995
+ "required": [
2996
+ "groupId"
2997
+ ]
2998
+ }
2999
+ }
3000
+ }
3001
+ },
3002
+ "parameters": [
3003
+ {
3004
+ "schema": {
3005
+ "type": "string",
3006
+ "format": "uuid",
3007
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3008
+ },
3009
+ "in": "path",
3010
+ "name": "id",
3011
+ "required": true
3012
+ }
3013
+ ],
3014
+ "security": [
3015
+ {
3016
+ "bearerAuth": []
3017
+ }
3018
+ ],
3019
+ "responses": {
3020
+ "200": {
3021
+ "description": "Default Response",
3022
+ "content": {
3023
+ "application/json": {
3024
+ "schema": {
3025
+ "type": "object",
3026
+ "properties": {
3027
+ "userId": {
3028
+ "type": "string",
3029
+ "format": "uuid",
3030
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3031
+ },
3032
+ "groupId": {
3033
+ "nullable": true,
3034
+ "type": "string",
3035
+ "format": "uuid",
3036
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3037
+ },
3038
+ "groupName": {
3039
+ "nullable": true,
3040
+ "type": "string"
3041
+ },
3042
+ "isOwner": {
3043
+ "type": "boolean"
3044
+ },
3045
+ "joinedAt": {
3046
+ "type": "string",
3047
+ "format": "date-time",
3048
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
3049
+ }
3050
+ },
3051
+ "required": [
3052
+ "userId",
3053
+ "groupId",
3054
+ "groupName",
3055
+ "isOwner",
3056
+ "joinedAt"
3057
+ ],
3058
+ "additionalProperties": false
3059
+ }
3060
+ }
3061
+ }
3062
+ }
3063
+ }
3064
+ }
3065
+ },
3066
+ "/groups": {
3067
+ "get": {
3068
+ "operationId": "listGroups",
3069
+ "summary": "List groups",
3070
+ "tags": [
3071
+ "groups"
3072
+ ],
3073
+ "security": [
3074
+ {
3075
+ "bearerAuth": []
3076
+ }
3077
+ ],
3078
+ "responses": {
3079
+ "200": {
3080
+ "description": "Default Response",
3081
+ "content": {
3082
+ "application/json": {
3083
+ "schema": {
3084
+ "type": "array",
3085
+ "items": {
3086
+ "type": "object",
3087
+ "properties": {
3088
+ "id": {
3089
+ "type": "string",
3090
+ "format": "uuid",
3091
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3092
+ },
3093
+ "name": {
3094
+ "type": "string"
3095
+ },
3096
+ "description": {
3097
+ "nullable": true,
3098
+ "type": "string"
3099
+ },
3100
+ "permissions": {
3101
+ "type": "array",
3102
+ "items": {
3103
+ "type": "string",
3104
+ "enum": [
3105
+ "thread.create",
3106
+ "thread.edit",
3107
+ "thread.delete",
3108
+ "thread.pin",
3109
+ "thread.lock",
3110
+ "post.create",
3111
+ "post.edit",
3112
+ "post.delete",
3113
+ "category.create",
3114
+ "category.edit",
3115
+ "category.delete",
3116
+ "user.ban",
3117
+ "group.create",
3118
+ "group.edit",
3119
+ "group.delete",
3120
+ "group.assign"
3121
+ ]
3122
+ }
3123
+ },
3124
+ "visibleCategoryIds": {
3125
+ "type": "array",
3126
+ "items": {
3127
+ "type": "string",
3128
+ "format": "uuid",
3129
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3130
+ }
3131
+ }
3132
+ },
3133
+ "required": [
3134
+ "id",
3135
+ "name",
3136
+ "description",
3137
+ "permissions",
3138
+ "visibleCategoryIds"
3139
+ ],
3140
+ "additionalProperties": false
3141
+ }
3142
+ }
3143
+ }
3144
+ }
3145
+ }
3146
+ }
3147
+ },
3148
+ "post": {
3149
+ "operationId": "createGroup",
3150
+ "summary": "Create a group (admin)",
3151
+ "tags": [
3152
+ "groups"
3153
+ ],
3154
+ "requestBody": {
3155
+ "required": true,
3156
+ "content": {
3157
+ "application/json": {
3158
+ "schema": {
3159
+ "type": "object",
3160
+ "properties": {
3161
+ "name": {
3162
+ "type": "string",
3163
+ "minLength": 1,
3164
+ "maxLength": 100
3165
+ },
3166
+ "description": {
3167
+ "nullable": true,
3168
+ "type": "string",
3169
+ "maxLength": 500
3170
+ }
3171
+ },
3172
+ "required": [
3173
+ "name"
3174
+ ]
3175
+ }
3176
+ }
3177
+ }
3178
+ },
3179
+ "security": [
3180
+ {
3181
+ "bearerAuth": []
3182
+ }
3183
+ ],
3184
+ "responses": {
3185
+ "201": {
3186
+ "description": "Default Response",
3187
+ "content": {
3188
+ "application/json": {
3189
+ "schema": {
3190
+ "type": "object",
3191
+ "properties": {
3192
+ "id": {
3193
+ "type": "string",
3194
+ "format": "uuid",
3195
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3196
+ },
3197
+ "name": {
3198
+ "type": "string"
3199
+ },
3200
+ "description": {
3201
+ "nullable": true,
3202
+ "type": "string"
3203
+ },
3204
+ "permissions": {
3205
+ "type": "array",
3206
+ "items": {
3207
+ "type": "string",
3208
+ "enum": [
3209
+ "thread.create",
3210
+ "thread.edit",
3211
+ "thread.delete",
3212
+ "thread.pin",
3213
+ "thread.lock",
3214
+ "post.create",
3215
+ "post.edit",
3216
+ "post.delete",
3217
+ "category.create",
3218
+ "category.edit",
3219
+ "category.delete",
3220
+ "user.ban",
3221
+ "group.create",
3222
+ "group.edit",
3223
+ "group.delete",
3224
+ "group.assign"
3225
+ ]
3226
+ }
3227
+ },
3228
+ "visibleCategoryIds": {
3229
+ "type": "array",
3230
+ "items": {
3231
+ "type": "string",
3232
+ "format": "uuid",
3233
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3234
+ }
3235
+ }
3236
+ },
3237
+ "required": [
3238
+ "id",
3239
+ "name",
3240
+ "description",
3241
+ "permissions",
3242
+ "visibleCategoryIds"
3243
+ ],
3244
+ "additionalProperties": false
3245
+ }
3246
+ }
3247
+ }
3248
+ }
3249
+ }
3250
+ }
3251
+ },
3252
+ "/groups/{id}": {
3253
+ "get": {
3254
+ "operationId": "getGroup",
3255
+ "summary": "Get a group",
3256
+ "tags": [
3257
+ "groups"
3258
+ ],
3259
+ "parameters": [
3260
+ {
3261
+ "schema": {
3262
+ "type": "string",
3263
+ "format": "uuid",
3264
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3265
+ },
3266
+ "in": "path",
3267
+ "name": "id",
3268
+ "required": true
3269
+ }
3270
+ ],
3271
+ "security": [
3272
+ {
3273
+ "bearerAuth": []
3274
+ }
3275
+ ],
3276
+ "responses": {
3277
+ "200": {
3278
+ "description": "Default Response",
3279
+ "content": {
3280
+ "application/json": {
3281
+ "schema": {
3282
+ "type": "object",
3283
+ "properties": {
3284
+ "id": {
3285
+ "type": "string",
3286
+ "format": "uuid",
3287
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3288
+ },
3289
+ "name": {
3290
+ "type": "string"
3291
+ },
3292
+ "description": {
3293
+ "nullable": true,
3294
+ "type": "string"
3295
+ },
3296
+ "permissions": {
3297
+ "type": "array",
3298
+ "items": {
3299
+ "type": "string",
3300
+ "enum": [
3301
+ "thread.create",
3302
+ "thread.edit",
3303
+ "thread.delete",
3304
+ "thread.pin",
3305
+ "thread.lock",
3306
+ "post.create",
3307
+ "post.edit",
3308
+ "post.delete",
3309
+ "category.create",
3310
+ "category.edit",
3311
+ "category.delete",
3312
+ "user.ban",
3313
+ "group.create",
3314
+ "group.edit",
3315
+ "group.delete",
3316
+ "group.assign"
3317
+ ]
3318
+ }
3319
+ },
3320
+ "visibleCategoryIds": {
3321
+ "type": "array",
3322
+ "items": {
3323
+ "type": "string",
3324
+ "format": "uuid",
3325
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3326
+ }
3327
+ }
3328
+ },
3329
+ "required": [
3330
+ "id",
3331
+ "name",
3332
+ "description",
3333
+ "permissions",
3334
+ "visibleCategoryIds"
3335
+ ],
3336
+ "additionalProperties": false
3337
+ }
3338
+ }
3339
+ }
3340
+ }
3341
+ }
3342
+ },
3343
+ "patch": {
3344
+ "operationId": "updateGroup",
3345
+ "summary": "Update a group (admin)",
3346
+ "tags": [
3347
+ "groups"
3348
+ ],
3349
+ "requestBody": {
3350
+ "required": true,
3351
+ "content": {
3352
+ "application/json": {
3353
+ "schema": {
3354
+ "type": "object",
3355
+ "properties": {
3356
+ "name": {
3357
+ "type": "string",
3358
+ "minLength": 1,
3359
+ "maxLength": 100
3360
+ },
3361
+ "description": {
3362
+ "nullable": true,
3363
+ "type": "string",
3364
+ "maxLength": 500
3365
+ }
3366
+ }
3367
+ }
3368
+ }
3369
+ }
3370
+ },
3371
+ "parameters": [
3372
+ {
3373
+ "schema": {
3374
+ "type": "string",
3375
+ "format": "uuid",
3376
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3377
+ },
3378
+ "in": "path",
3379
+ "name": "id",
3380
+ "required": true
3381
+ }
3382
+ ],
3383
+ "security": [
3384
+ {
3385
+ "bearerAuth": []
3386
+ }
3387
+ ],
3388
+ "responses": {
3389
+ "200": {
3390
+ "description": "Default Response",
3391
+ "content": {
3392
+ "application/json": {
3393
+ "schema": {
3394
+ "type": "object",
3395
+ "properties": {
3396
+ "id": {
3397
+ "type": "string",
3398
+ "format": "uuid",
3399
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3400
+ },
3401
+ "name": {
3402
+ "type": "string"
3403
+ },
3404
+ "description": {
3405
+ "nullable": true,
3406
+ "type": "string"
3407
+ },
3408
+ "permissions": {
3409
+ "type": "array",
3410
+ "items": {
3411
+ "type": "string",
3412
+ "enum": [
3413
+ "thread.create",
3414
+ "thread.edit",
3415
+ "thread.delete",
3416
+ "thread.pin",
3417
+ "thread.lock",
3418
+ "post.create",
3419
+ "post.edit",
3420
+ "post.delete",
3421
+ "category.create",
3422
+ "category.edit",
3423
+ "category.delete",
3424
+ "user.ban",
3425
+ "group.create",
3426
+ "group.edit",
3427
+ "group.delete",
3428
+ "group.assign"
3429
+ ]
3430
+ }
3431
+ },
3432
+ "visibleCategoryIds": {
3433
+ "type": "array",
3434
+ "items": {
3435
+ "type": "string",
3436
+ "format": "uuid",
3437
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3438
+ }
3439
+ }
3440
+ },
3441
+ "required": [
3442
+ "id",
3443
+ "name",
3444
+ "description",
3445
+ "permissions",
3446
+ "visibleCategoryIds"
3447
+ ],
3448
+ "additionalProperties": false
3449
+ }
3450
+ }
3451
+ }
3452
+ }
3453
+ }
3454
+ },
3455
+ "delete": {
3456
+ "operationId": "deleteGroup",
3457
+ "summary": "Delete a group (admin)",
3458
+ "tags": [
3459
+ "groups"
3460
+ ],
3461
+ "parameters": [
3462
+ {
3463
+ "schema": {
3464
+ "type": "string",
3465
+ "format": "uuid",
3466
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3467
+ },
3468
+ "in": "path",
3469
+ "name": "id",
3470
+ "required": true
3471
+ }
3472
+ ],
3473
+ "security": [
3474
+ {
3475
+ "bearerAuth": []
3476
+ }
3477
+ ],
3478
+ "responses": {
3479
+ "204": {
3480
+ "description": "Default Response",
3481
+ "content": {
3482
+ "application/json": {
3483
+ "schema": {
3484
+ "type": "string",
3485
+ "nullable": true,
3486
+ "enum": [
3487
+ null
3488
+ ]
3489
+ }
3490
+ }
3491
+ }
3492
+ }
3493
+ }
3494
+ }
3495
+ },
3496
+ "/groups/{id}/permissions": {
3497
+ "put": {
3498
+ "operationId": "setGroupPermissions",
3499
+ "summary": "Replace all permissions for a group (admin)",
3500
+ "tags": [
3501
+ "groups"
3502
+ ],
3503
+ "requestBody": {
3504
+ "required": true,
3505
+ "content": {
3506
+ "application/json": {
3507
+ "schema": {
3508
+ "type": "object",
3509
+ "properties": {
3510
+ "permissions": {
3511
+ "type": "array",
3512
+ "items": {
3513
+ "type": "string",
3514
+ "enum": [
3515
+ "thread.create",
3516
+ "thread.edit",
3517
+ "thread.delete",
3518
+ "thread.pin",
3519
+ "thread.lock",
3520
+ "post.create",
3521
+ "post.edit",
3522
+ "post.delete",
3523
+ "category.create",
3524
+ "category.edit",
3525
+ "category.delete",
3526
+ "user.ban",
3527
+ "group.create",
3528
+ "group.edit",
3529
+ "group.delete",
3530
+ "group.assign"
3531
+ ]
3532
+ }
3533
+ }
3534
+ },
3535
+ "required": [
3536
+ "permissions"
3537
+ ]
3538
+ }
3539
+ }
3540
+ }
3541
+ },
3542
+ "parameters": [
3543
+ {
3544
+ "schema": {
3545
+ "type": "string",
3546
+ "format": "uuid",
3547
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3548
+ },
3549
+ "in": "path",
3550
+ "name": "id",
3551
+ "required": true
3552
+ }
3553
+ ],
3554
+ "security": [
3555
+ {
3556
+ "bearerAuth": []
3557
+ }
3558
+ ],
3559
+ "responses": {
3560
+ "200": {
3561
+ "description": "Default Response",
3562
+ "content": {
3563
+ "application/json": {
3564
+ "schema": {
3565
+ "type": "object",
3566
+ "properties": {
3567
+ "id": {
3568
+ "type": "string",
3569
+ "format": "uuid",
3570
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3571
+ },
3572
+ "name": {
3573
+ "type": "string"
3574
+ },
3575
+ "description": {
3576
+ "nullable": true,
3577
+ "type": "string"
3578
+ },
3579
+ "permissions": {
3580
+ "type": "array",
3581
+ "items": {
3582
+ "type": "string",
3583
+ "enum": [
3584
+ "thread.create",
3585
+ "thread.edit",
3586
+ "thread.delete",
3587
+ "thread.pin",
3588
+ "thread.lock",
3589
+ "post.create",
3590
+ "post.edit",
3591
+ "post.delete",
3592
+ "category.create",
3593
+ "category.edit",
3594
+ "category.delete",
3595
+ "user.ban",
3596
+ "group.create",
3597
+ "group.edit",
3598
+ "group.delete",
3599
+ "group.assign"
3600
+ ]
3601
+ }
3602
+ },
3603
+ "visibleCategoryIds": {
3604
+ "type": "array",
3605
+ "items": {
3606
+ "type": "string",
3607
+ "format": "uuid",
3608
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3609
+ }
3610
+ }
3611
+ },
3612
+ "required": [
3613
+ "id",
3614
+ "name",
3615
+ "description",
3616
+ "permissions",
3617
+ "visibleCategoryIds"
3618
+ ],
3619
+ "additionalProperties": false
3620
+ }
3621
+ }
3622
+ }
3623
+ }
3624
+ }
3625
+ }
3626
+ },
3627
+ "/groups/{id}/categories": {
3628
+ "put": {
3629
+ "operationId": "setGroupCategories",
3630
+ "summary": "Set category visibility for a group (admin)",
3631
+ "tags": [
3632
+ "groups"
3633
+ ],
3634
+ "requestBody": {
3635
+ "required": true,
3636
+ "content": {
3637
+ "application/json": {
3638
+ "schema": {
3639
+ "type": "object",
3640
+ "properties": {
3641
+ "categoryIds": {
3642
+ "type": "array",
3643
+ "items": {
3644
+ "type": "string",
3645
+ "format": "uuid",
3646
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3647
+ }
3648
+ }
3649
+ },
3650
+ "required": [
3651
+ "categoryIds"
3652
+ ]
3653
+ }
3654
+ }
3655
+ }
3656
+ },
3657
+ "parameters": [
3658
+ {
3659
+ "schema": {
3660
+ "type": "string",
3661
+ "format": "uuid",
3662
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3663
+ },
3664
+ "in": "path",
3665
+ "name": "id",
3666
+ "required": true
3667
+ }
3668
+ ],
3669
+ "security": [
3670
+ {
3671
+ "bearerAuth": []
3672
+ }
3673
+ ],
3674
+ "responses": {
3675
+ "200": {
3676
+ "description": "Default Response",
3677
+ "content": {
3678
+ "application/json": {
3679
+ "schema": {
3680
+ "type": "object",
3681
+ "properties": {
3682
+ "id": {
3683
+ "type": "string",
3684
+ "format": "uuid",
3685
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3686
+ },
3687
+ "name": {
3688
+ "type": "string"
3689
+ },
3690
+ "description": {
3691
+ "nullable": true,
3692
+ "type": "string"
3693
+ },
3694
+ "permissions": {
3695
+ "type": "array",
3696
+ "items": {
3697
+ "type": "string",
3698
+ "enum": [
3699
+ "thread.create",
3700
+ "thread.edit",
3701
+ "thread.delete",
3702
+ "thread.pin",
3703
+ "thread.lock",
3704
+ "post.create",
3705
+ "post.edit",
3706
+ "post.delete",
3707
+ "category.create",
3708
+ "category.edit",
3709
+ "category.delete",
3710
+ "user.ban",
3711
+ "group.create",
3712
+ "group.edit",
3713
+ "group.delete",
3714
+ "group.assign"
3715
+ ]
3716
+ }
3717
+ },
3718
+ "visibleCategoryIds": {
3719
+ "type": "array",
3720
+ "items": {
3721
+ "type": "string",
3722
+ "format": "uuid",
3723
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3724
+ }
3725
+ }
3726
+ },
3727
+ "required": [
3728
+ "id",
3729
+ "name",
3730
+ "description",
3731
+ "permissions",
3732
+ "visibleCategoryIds"
3733
+ ],
3734
+ "additionalProperties": false
3735
+ }
3736
+ }
3737
+ }
3738
+ }
3739
+ }
3740
+ }
3741
+ },
3742
+ "/search": {
3743
+ "get": {
3744
+ "operationId": "search",
3745
+ "summary": "Full-text search across threads and posts (visible categories only)",
3746
+ "tags": [
3747
+ "search"
3748
+ ],
3749
+ "parameters": [
3750
+ {
3751
+ "schema": {
3752
+ "type": "string",
3753
+ "minLength": 1,
3754
+ "maxLength": 500
3755
+ },
3756
+ "in": "query",
3757
+ "name": "q",
3758
+ "required": true
3759
+ },
3760
+ {
3761
+ "schema": {
3762
+ "type": "string",
3763
+ "format": "uuid",
3764
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3765
+ },
3766
+ "in": "query",
3767
+ "name": "categoryId",
3768
+ "required": false
3769
+ },
3770
+ {
3771
+ "schema": {
3772
+ "default": 20,
3773
+ "type": "integer",
3774
+ "minimum": 1,
3775
+ "maximum": 50
3776
+ },
3777
+ "in": "query",
3778
+ "name": "limit",
3779
+ "required": false
3780
+ }
3781
+ ],
3782
+ "responses": {
3783
+ "200": {
3784
+ "description": "Default Response",
3785
+ "content": {
3786
+ "application/json": {
3787
+ "schema": {
3788
+ "type": "object",
3789
+ "properties": {
3790
+ "results": {
3791
+ "type": "array",
3792
+ "items": {
3793
+ "type": "object",
3794
+ "properties": {
3795
+ "type": {
3796
+ "type": "string",
3797
+ "enum": [
3798
+ "thread",
3799
+ "post"
3800
+ ]
3801
+ },
3802
+ "id": {
3803
+ "type": "string",
3804
+ "format": "uuid",
3805
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3806
+ },
3807
+ "threadId": {
3808
+ "type": "string",
3809
+ "format": "uuid",
3810
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3811
+ },
3812
+ "categoryId": {
3813
+ "type": "string",
3814
+ "format": "uuid",
3815
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3816
+ },
3817
+ "title": {
3818
+ "nullable": true,
3819
+ "type": "string"
3820
+ },
3821
+ "excerpt": {
3822
+ "type": "string"
3823
+ },
3824
+ "authorId": {
3825
+ "type": "string",
3826
+ "format": "uuid",
3827
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
3828
+ },
3829
+ "createdAt": {
3830
+ "type": "string",
3831
+ "format": "date-time",
3832
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
3833
+ }
3834
+ },
3835
+ "required": [
3836
+ "type",
3837
+ "id",
3838
+ "threadId",
3839
+ "categoryId",
3840
+ "title",
3841
+ "excerpt",
3842
+ "authorId",
3843
+ "createdAt"
3844
+ ],
3845
+ "additionalProperties": false
3846
+ }
3847
+ },
3848
+ "total": {
3849
+ "type": "integer",
3850
+ "minimum": -9007199254740991,
3851
+ "maximum": 9007199254740991
3852
+ }
3853
+ },
3854
+ "required": [
3855
+ "results",
3856
+ "total"
3857
+ ],
3858
+ "additionalProperties": false
3859
+ }
3860
+ }
3861
+ }
3862
+ }
3863
+ }
3864
+ }
3865
+ },
3866
+ "/health": {
3867
+ "get": {
3868
+ "summary": "Health check",
3869
+ "tags": [
3870
+ "system"
3871
+ ],
3872
+ "responses": {
3873
+ "200": {
3874
+ "description": "Default Response"
3875
+ }
3876
+ }
3877
+ }
3878
+ }
3879
+ }
3880
+ }