@quizbase/client 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.
@@ -0,0 +1,1809 @@
1
+ /**
2
+ * Telemetry event emitted after every HTTP attempt (including retries).
3
+ * Wire it to your observability stack of choice (PostHog, Datadog, Sentry breadcrumbs, ...).
4
+ */
5
+ interface RequestEvent {
6
+ /** HTTP method, uppercase. */
7
+ method: string;
8
+ /** Logical endpoint key (e.g. `questions.random`). Stable across path-param values. */
9
+ endpoint: string;
10
+ /** Full URL hit (with query string). */
11
+ url: string;
12
+ /** Wall-clock duration of this attempt in milliseconds. */
13
+ duration: number;
14
+ /** HTTP status code, or `0` if the request never reached a response (network/timeout). */
15
+ status: number;
16
+ /** Server-issued `X-Request-Id`, if present. Useful for support requests. */
17
+ requestId: string | null;
18
+ /** Number of retries already attempted before this one. `0` for the first try. */
19
+ retryCount: number;
20
+ /** Final attempt that the caller will see, after retry loop terminates. */
21
+ final: boolean;
22
+ /** Error if this attempt threw (network / timeout / non-2xx). */
23
+ error?: Error;
24
+ }
25
+ type OnRequestHook = (event: RequestEvent) => void | Promise<void>;
26
+
27
+ /**
28
+ * This file was auto-generated by openapi-typescript.
29
+ * Do not make direct changes to the file.
30
+ */
31
+ interface paths {
32
+ "/api/v1/categories": {
33
+ parameters: {
34
+ query?: never;
35
+ header?: never;
36
+ path?: never;
37
+ cookie?: never;
38
+ };
39
+ /**
40
+ * List categories
41
+ * @description 24 top-level categories with localized names. Public, no API key required.
42
+ */
43
+ get: {
44
+ parameters: {
45
+ query?: {
46
+ /** @description Display language for category names + slug labels (subcategories/tags) */
47
+ lang?: "en" | "pl";
48
+ };
49
+ header?: never;
50
+ path?: never;
51
+ cookie?: never;
52
+ };
53
+ requestBody?: never;
54
+ responses: {
55
+ /** @description Categories list. */
56
+ 200: {
57
+ headers: {
58
+ [name: string]: unknown;
59
+ };
60
+ content: {
61
+ "application/json": components["schemas"]["CategoriesResponse"];
62
+ };
63
+ };
64
+ /** @description Invalid query parameters. */
65
+ 400: {
66
+ headers: {
67
+ [name: string]: unknown;
68
+ };
69
+ content: {
70
+ "application/problem+json": components["schemas"]["ProblemDetails"];
71
+ };
72
+ };
73
+ /** @description Rate limit exceeded. */
74
+ 429: {
75
+ headers: {
76
+ [name: string]: unknown;
77
+ };
78
+ content: {
79
+ "application/problem+json": components["schemas"]["ProblemDetails"];
80
+ };
81
+ };
82
+ /** @description Server error. */
83
+ 500: {
84
+ headers: {
85
+ [name: string]: unknown;
86
+ };
87
+ content: {
88
+ "application/problem+json": components["schemas"]["ProblemDetails"];
89
+ };
90
+ };
91
+ };
92
+ };
93
+ put?: never;
94
+ post?: never;
95
+ delete?: never;
96
+ options?: never;
97
+ head?: never;
98
+ patch?: never;
99
+ trace?: never;
100
+ };
101
+ "/api/v1/stats": {
102
+ parameters: {
103
+ query?: never;
104
+ header?: never;
105
+ path?: never;
106
+ cookie?: never;
107
+ };
108
+ /**
109
+ * Aggregate dataset stats
110
+ * @description Total questions, per-language, per-source, per-category counters. Public, cached.
111
+ */
112
+ get: {
113
+ parameters: {
114
+ query?: {
115
+ /** @description Display language for category names + slug labels (subcategories/tags) */
116
+ lang?: "en" | "pl";
117
+ };
118
+ header?: never;
119
+ path?: never;
120
+ cookie?: never;
121
+ };
122
+ requestBody?: never;
123
+ responses: {
124
+ /** @description Stats snapshot. */
125
+ 200: {
126
+ headers: {
127
+ [name: string]: unknown;
128
+ };
129
+ content: {
130
+ "application/json": components["schemas"]["StatsResponse"];
131
+ };
132
+ };
133
+ /** @description Invalid query parameters. */
134
+ 400: {
135
+ headers: {
136
+ [name: string]: unknown;
137
+ };
138
+ content: {
139
+ "application/problem+json": components["schemas"]["ProblemDetails"];
140
+ };
141
+ };
142
+ /** @description Rate limit exceeded. */
143
+ 429: {
144
+ headers: {
145
+ [name: string]: unknown;
146
+ };
147
+ content: {
148
+ "application/problem+json": components["schemas"]["ProblemDetails"];
149
+ };
150
+ };
151
+ /** @description Server error. */
152
+ 500: {
153
+ headers: {
154
+ [name: string]: unknown;
155
+ };
156
+ content: {
157
+ "application/problem+json": components["schemas"]["ProblemDetails"];
158
+ };
159
+ };
160
+ };
161
+ };
162
+ put?: never;
163
+ post?: never;
164
+ delete?: never;
165
+ options?: never;
166
+ head?: never;
167
+ patch?: never;
168
+ trace?: never;
169
+ };
170
+ "/api/v1/languages": {
171
+ parameters: {
172
+ query?: never;
173
+ header?: never;
174
+ path?: never;
175
+ cookie?: never;
176
+ };
177
+ /**
178
+ * Supported languages
179
+ * @description Whitelist of `?lang=` values with native names and counts. Public.
180
+ */
181
+ get: {
182
+ parameters: {
183
+ query?: {
184
+ /** @description Display language for category names + slug labels (subcategories/tags) */
185
+ lang?: "en" | "pl";
186
+ };
187
+ header?: never;
188
+ path?: never;
189
+ cookie?: never;
190
+ };
191
+ requestBody?: never;
192
+ responses: {
193
+ /** @description Supported languages. */
194
+ 200: {
195
+ headers: {
196
+ [name: string]: unknown;
197
+ };
198
+ content: {
199
+ "application/json": components["schemas"]["LanguagesResponse"];
200
+ };
201
+ };
202
+ /** @description Invalid query parameters. */
203
+ 400: {
204
+ headers: {
205
+ [name: string]: unknown;
206
+ };
207
+ content: {
208
+ "application/problem+json": components["schemas"]["ProblemDetails"];
209
+ };
210
+ };
211
+ /** @description Rate limit exceeded. */
212
+ 429: {
213
+ headers: {
214
+ [name: string]: unknown;
215
+ };
216
+ content: {
217
+ "application/problem+json": components["schemas"]["ProblemDetails"];
218
+ };
219
+ };
220
+ /** @description Server error. */
221
+ 500: {
222
+ headers: {
223
+ [name: string]: unknown;
224
+ };
225
+ content: {
226
+ "application/problem+json": components["schemas"]["ProblemDetails"];
227
+ };
228
+ };
229
+ };
230
+ };
231
+ put?: never;
232
+ post?: never;
233
+ delete?: never;
234
+ options?: never;
235
+ head?: never;
236
+ patch?: never;
237
+ trace?: never;
238
+ };
239
+ "/api/v1/topics": {
240
+ parameters: {
241
+ query?: never;
242
+ header?: never;
243
+ path?: never;
244
+ cookie?: never;
245
+ };
246
+ /**
247
+ * List curated topics
248
+ * @description 2,184 curated topics with aliases and counts. Public, MV-backed.
249
+ */
250
+ get: {
251
+ parameters: {
252
+ query?: {
253
+ /** @description Display language for category names + slug labels (subcategories/tags) */
254
+ lang?: "en" | "pl";
255
+ q?: string;
256
+ kind?: "tag" | "subcategory";
257
+ cursor?: string;
258
+ limit?: number;
259
+ };
260
+ header?: never;
261
+ path?: never;
262
+ cookie?: never;
263
+ };
264
+ requestBody?: never;
265
+ responses: {
266
+ /** @description Curated topics. */
267
+ 200: {
268
+ headers: {
269
+ [name: string]: unknown;
270
+ };
271
+ content: {
272
+ "application/json": components["schemas"]["TopicsListResponse"];
273
+ };
274
+ };
275
+ /** @description Invalid query parameters. */
276
+ 400: {
277
+ headers: {
278
+ [name: string]: unknown;
279
+ };
280
+ content: {
281
+ "application/problem+json": components["schemas"]["ProblemDetails"];
282
+ };
283
+ };
284
+ /** @description Rate limit exceeded. */
285
+ 429: {
286
+ headers: {
287
+ [name: string]: unknown;
288
+ };
289
+ content: {
290
+ "application/problem+json": components["schemas"]["ProblemDetails"];
291
+ };
292
+ };
293
+ /** @description Server error. */
294
+ 500: {
295
+ headers: {
296
+ [name: string]: unknown;
297
+ };
298
+ content: {
299
+ "application/problem+json": components["schemas"]["ProblemDetails"];
300
+ };
301
+ };
302
+ };
303
+ };
304
+ put?: never;
305
+ post?: never;
306
+ delete?: never;
307
+ options?: never;
308
+ head?: never;
309
+ patch?: never;
310
+ trace?: never;
311
+ };
312
+ "/api/v1/topics/{slug}": {
313
+ parameters: {
314
+ query?: never;
315
+ header?: never;
316
+ path?: never;
317
+ cookie?: never;
318
+ };
319
+ /**
320
+ * Topic detail with facets
321
+ * @description Single topic + facets (byCategory, byDifficulty, byLanguage, coOccurringTags, coOccurringSubcategories) + 3 sample questions.
322
+ */
323
+ get: {
324
+ parameters: {
325
+ query?: {
326
+ /** @description Display language for category names + slug labels (subcategories/tags) */
327
+ lang?: "en" | "pl";
328
+ };
329
+ header?: never;
330
+ path: {
331
+ /** @description Curated topic slug or alias. */
332
+ slug: string;
333
+ };
334
+ cookie?: never;
335
+ };
336
+ requestBody?: never;
337
+ responses: {
338
+ /** @description Topic detail. */
339
+ 200: {
340
+ headers: {
341
+ [name: string]: unknown;
342
+ };
343
+ content: {
344
+ "application/json": components["schemas"]["TopicDetailResponse"];
345
+ };
346
+ };
347
+ /** @description Invalid query parameters. */
348
+ 400: {
349
+ headers: {
350
+ [name: string]: unknown;
351
+ };
352
+ content: {
353
+ "application/problem+json": components["schemas"]["ProblemDetails"];
354
+ };
355
+ };
356
+ /** @description Topic not found. */
357
+ 404: {
358
+ headers: {
359
+ [name: string]: unknown;
360
+ };
361
+ content: {
362
+ "application/problem+json": components["schemas"]["ProblemDetails"];
363
+ };
364
+ };
365
+ /** @description Rate limit exceeded. */
366
+ 429: {
367
+ headers: {
368
+ [name: string]: unknown;
369
+ };
370
+ content: {
371
+ "application/problem+json": components["schemas"]["ProblemDetails"];
372
+ };
373
+ };
374
+ /** @description Server error. */
375
+ 500: {
376
+ headers: {
377
+ [name: string]: unknown;
378
+ };
379
+ content: {
380
+ "application/problem+json": components["schemas"]["ProblemDetails"];
381
+ };
382
+ };
383
+ };
384
+ };
385
+ put?: never;
386
+ post?: never;
387
+ delete?: never;
388
+ options?: never;
389
+ head?: never;
390
+ patch?: never;
391
+ trace?: never;
392
+ };
393
+ "/api/v1/tags": {
394
+ parameters: {
395
+ query?: never;
396
+ header?: never;
397
+ path?: never;
398
+ cookie?: never;
399
+ };
400
+ /**
401
+ * List raw tags
402
+ * @description Raw tags with display labels and counts. Threshold ≥5 questions.
403
+ */
404
+ get: {
405
+ parameters: {
406
+ query?: {
407
+ /** @description Display language for category names + slug labels (subcategories/tags) */
408
+ lang?: "en" | "pl";
409
+ q?: string;
410
+ cursor?: string;
411
+ limit?: number;
412
+ };
413
+ header?: never;
414
+ path?: never;
415
+ cookie?: never;
416
+ };
417
+ requestBody?: never;
418
+ responses: {
419
+ /** @description Raw tags. */
420
+ 200: {
421
+ headers: {
422
+ [name: string]: unknown;
423
+ };
424
+ content: {
425
+ "application/json": components["schemas"]["TagsListResponse"];
426
+ };
427
+ };
428
+ /** @description Invalid query parameters. */
429
+ 400: {
430
+ headers: {
431
+ [name: string]: unknown;
432
+ };
433
+ content: {
434
+ "application/problem+json": components["schemas"]["ProblemDetails"];
435
+ };
436
+ };
437
+ /** @description Rate limit exceeded. */
438
+ 429: {
439
+ headers: {
440
+ [name: string]: unknown;
441
+ };
442
+ content: {
443
+ "application/problem+json": components["schemas"]["ProblemDetails"];
444
+ };
445
+ };
446
+ /** @description Server error. */
447
+ 500: {
448
+ headers: {
449
+ [name: string]: unknown;
450
+ };
451
+ content: {
452
+ "application/problem+json": components["schemas"]["ProblemDetails"];
453
+ };
454
+ };
455
+ };
456
+ };
457
+ put?: never;
458
+ post?: never;
459
+ delete?: never;
460
+ options?: never;
461
+ head?: never;
462
+ patch?: never;
463
+ trace?: never;
464
+ };
465
+ "/api/v1/subcategories": {
466
+ parameters: {
467
+ query?: never;
468
+ header?: never;
469
+ path?: never;
470
+ cookie?: never;
471
+ };
472
+ /**
473
+ * List raw subcategories
474
+ * @description Raw subcategories layer. Threshold ≥5 questions.
475
+ */
476
+ get: {
477
+ parameters: {
478
+ query?: {
479
+ /** @description Display language for category names + slug labels (subcategories/tags) */
480
+ lang?: "en" | "pl";
481
+ q?: string;
482
+ cursor?: string;
483
+ limit?: number;
484
+ };
485
+ header?: never;
486
+ path?: never;
487
+ cookie?: never;
488
+ };
489
+ requestBody?: never;
490
+ responses: {
491
+ /** @description Raw subcategories. */
492
+ 200: {
493
+ headers: {
494
+ [name: string]: unknown;
495
+ };
496
+ content: {
497
+ "application/json": components["schemas"]["SubcategoriesListResponse"];
498
+ };
499
+ };
500
+ /** @description Invalid query parameters. */
501
+ 400: {
502
+ headers: {
503
+ [name: string]: unknown;
504
+ };
505
+ content: {
506
+ "application/problem+json": components["schemas"]["ProblemDetails"];
507
+ };
508
+ };
509
+ /** @description Rate limit exceeded. */
510
+ 429: {
511
+ headers: {
512
+ [name: string]: unknown;
513
+ };
514
+ content: {
515
+ "application/problem+json": components["schemas"]["ProblemDetails"];
516
+ };
517
+ };
518
+ /** @description Server error. */
519
+ 500: {
520
+ headers: {
521
+ [name: string]: unknown;
522
+ };
523
+ content: {
524
+ "application/problem+json": components["schemas"]["ProblemDetails"];
525
+ };
526
+ };
527
+ };
528
+ };
529
+ put?: never;
530
+ post?: never;
531
+ delete?: never;
532
+ options?: never;
533
+ head?: never;
534
+ patch?: never;
535
+ trace?: never;
536
+ };
537
+ "/api/v1/report": {
538
+ parameters: {
539
+ query?: never;
540
+ header?: never;
541
+ path?: never;
542
+ cookie?: never;
543
+ };
544
+ get?: never;
545
+ put?: never;
546
+ /**
547
+ * Report a problem with a question
548
+ * @description Public submission for translation / factual / inappropriate / attribution / other reports. Rate-limited 5/min/IP.
549
+ */
550
+ post: {
551
+ parameters: {
552
+ query?: never;
553
+ header?: never;
554
+ path?: never;
555
+ cookie?: never;
556
+ };
557
+ requestBody: {
558
+ content: {
559
+ "application/json": components["schemas"]["ReportRequest"];
560
+ };
561
+ };
562
+ responses: {
563
+ /** @description Report accepted. */
564
+ 202: {
565
+ headers: {
566
+ [name: string]: unknown;
567
+ };
568
+ content: {
569
+ "application/json": components["schemas"]["ReportAcceptedResponse"];
570
+ };
571
+ };
572
+ /** @description Invalid body — provide one of `questionId`, `questionText`, `questionUrl`. */
573
+ 400: {
574
+ headers: {
575
+ [name: string]: unknown;
576
+ };
577
+ content: {
578
+ "application/problem+json": components["schemas"]["ProblemDetails"];
579
+ };
580
+ };
581
+ /** @description Question not found (when `questionId` provided). */
582
+ 404: {
583
+ headers: {
584
+ [name: string]: unknown;
585
+ };
586
+ content: {
587
+ "application/problem+json": components["schemas"]["ProblemDetails"];
588
+ };
589
+ };
590
+ /** @description Rate limit exceeded (5/min/IP). */
591
+ 429: {
592
+ headers: {
593
+ [name: string]: unknown;
594
+ };
595
+ content: {
596
+ "application/problem+json": components["schemas"]["ProblemDetails"];
597
+ };
598
+ };
599
+ /** @description Server error. */
600
+ 500: {
601
+ headers: {
602
+ [name: string]: unknown;
603
+ };
604
+ content: {
605
+ "application/problem+json": components["schemas"]["ProblemDetails"];
606
+ };
607
+ };
608
+ };
609
+ };
610
+ delete?: never;
611
+ options?: never;
612
+ head?: never;
613
+ patch?: never;
614
+ trace?: never;
615
+ };
616
+ "/api/v1/me": {
617
+ parameters: {
618
+ query?: never;
619
+ header?: never;
620
+ path?: never;
621
+ cookie?: never;
622
+ };
623
+ /**
624
+ * API key info
625
+ * @description Confirms your key, tier, and current rate-limit window. The first endpoint to hit when wiring a new client.
626
+ */
627
+ get: {
628
+ parameters: {
629
+ query?: never;
630
+ header?: never;
631
+ path?: never;
632
+ cookie?: never;
633
+ };
634
+ requestBody?: never;
635
+ responses: {
636
+ /** @description Key info. */
637
+ 200: {
638
+ headers: {
639
+ [name: string]: unknown;
640
+ };
641
+ content: {
642
+ "application/json": components["schemas"]["MeResponse"];
643
+ };
644
+ };
645
+ /** @description Missing, malformed, invalid, or revoked API key. */
646
+ 401: {
647
+ headers: {
648
+ [name: string]: unknown;
649
+ };
650
+ content: {
651
+ "application/problem+json": components["schemas"]["ProblemDetails"];
652
+ };
653
+ };
654
+ /** @description IP not allowed (per-key allowlist). */
655
+ 403: {
656
+ headers: {
657
+ [name: string]: unknown;
658
+ };
659
+ content: {
660
+ "application/problem+json": components["schemas"]["ProblemDetails"];
661
+ };
662
+ };
663
+ /** @description Rate limit exceeded. See `Retry-After` header. */
664
+ 429: {
665
+ headers: {
666
+ [name: string]: unknown;
667
+ };
668
+ content: {
669
+ "application/problem+json": components["schemas"]["ProblemDetails"];
670
+ };
671
+ };
672
+ /** @description Server error. */
673
+ 500: {
674
+ headers: {
675
+ [name: string]: unknown;
676
+ };
677
+ content: {
678
+ "application/problem+json": components["schemas"]["ProblemDetails"];
679
+ };
680
+ };
681
+ };
682
+ };
683
+ put?: never;
684
+ post?: never;
685
+ delete?: never;
686
+ options?: never;
687
+ head?: never;
688
+ patch?: never;
689
+ trace?: never;
690
+ };
691
+ "/api/v1/usage": {
692
+ parameters: {
693
+ query?: never;
694
+ header?: never;
695
+ path?: never;
696
+ cookie?: never;
697
+ };
698
+ /**
699
+ * 30-day usage series
700
+ * @description Daily request counts + summary across all your keys.
701
+ */
702
+ get: {
703
+ parameters: {
704
+ query?: {
705
+ /** @description Trailing days, 1-90. */
706
+ days?: number;
707
+ };
708
+ header?: never;
709
+ path?: never;
710
+ cookie?: never;
711
+ };
712
+ requestBody?: never;
713
+ responses: {
714
+ /** @description Usage payload. */
715
+ 200: {
716
+ headers: {
717
+ [name: string]: unknown;
718
+ };
719
+ content: {
720
+ "application/json": components["schemas"]["UsageResponse"];
721
+ };
722
+ };
723
+ /** @description Missing, malformed, invalid, or revoked API key. */
724
+ 401: {
725
+ headers: {
726
+ [name: string]: unknown;
727
+ };
728
+ content: {
729
+ "application/problem+json": components["schemas"]["ProblemDetails"];
730
+ };
731
+ };
732
+ /** @description IP not allowed (per-key allowlist). */
733
+ 403: {
734
+ headers: {
735
+ [name: string]: unknown;
736
+ };
737
+ content: {
738
+ "application/problem+json": components["schemas"]["ProblemDetails"];
739
+ };
740
+ };
741
+ /** @description Rate limit exceeded. See `Retry-After` header. */
742
+ 429: {
743
+ headers: {
744
+ [name: string]: unknown;
745
+ };
746
+ content: {
747
+ "application/problem+json": components["schemas"]["ProblemDetails"];
748
+ };
749
+ };
750
+ /** @description Server error. */
751
+ 500: {
752
+ headers: {
753
+ [name: string]: unknown;
754
+ };
755
+ content: {
756
+ "application/problem+json": components["schemas"]["ProblemDetails"];
757
+ };
758
+ };
759
+ };
760
+ };
761
+ put?: never;
762
+ post?: never;
763
+ delete?: never;
764
+ options?: never;
765
+ head?: never;
766
+ patch?: never;
767
+ trace?: never;
768
+ };
769
+ "/api/v1/questions": {
770
+ parameters: {
771
+ query?: never;
772
+ header?: never;
773
+ path?: never;
774
+ cookie?: never;
775
+ };
776
+ /**
777
+ * Browse questions (cursor pagination)
778
+ * @description Paginated browse with filters and delta-sync via `updated_since`.
779
+ */
780
+ get: {
781
+ parameters: {
782
+ query?: {
783
+ cursor?: string;
784
+ limit?: number;
785
+ /** @description Display language for category names + slug labels (subcategories/tags) */
786
+ lang?: "en" | "pl";
787
+ updated_since?: string | null;
788
+ category?: string;
789
+ difficulty?: "easy" | "medium" | "hard";
790
+ type?: "multiple" | "boolean" | "text_input";
791
+ tags?: string;
792
+ tags_any?: string;
793
+ topic?: string;
794
+ topics_any?: string;
795
+ subcategory?: string;
796
+ quality?: "high";
797
+ regions?: string;
798
+ source?: "opentdb" | "opentriviaqa" | "mkqa" | "mmlu-prox" | "wikipedia" | "community" | "runriva" | "ai-generated" | "purchased" | "quizbase";
799
+ license?: "CC-BY-SA-4.0" | "CC-BY-SA-3.0" | "CC-BY-4.0" | "MIT" | "proprietary";
800
+ count?: "estimate" | "exact" | "none";
801
+ };
802
+ header?: never;
803
+ path?: never;
804
+ cookie?: never;
805
+ };
806
+ requestBody?: never;
807
+ responses: {
808
+ /** @description Questions page. */
809
+ 200: {
810
+ headers: {
811
+ [name: string]: unknown;
812
+ };
813
+ content: {
814
+ "application/json": components["schemas"]["QuestionsListResponse"];
815
+ };
816
+ };
817
+ /** @description Invalid query parameters. */
818
+ 400: {
819
+ headers: {
820
+ [name: string]: unknown;
821
+ };
822
+ content: {
823
+ "application/problem+json": components["schemas"]["ProblemDetails"];
824
+ };
825
+ };
826
+ /** @description Missing, malformed, invalid, or revoked API key. */
827
+ 401: {
828
+ headers: {
829
+ [name: string]: unknown;
830
+ };
831
+ content: {
832
+ "application/problem+json": components["schemas"]["ProblemDetails"];
833
+ };
834
+ };
835
+ /** @description IP not allowed (per-key allowlist). */
836
+ 403: {
837
+ headers: {
838
+ [name: string]: unknown;
839
+ };
840
+ content: {
841
+ "application/problem+json": components["schemas"]["ProblemDetails"];
842
+ };
843
+ };
844
+ /** @description Rate limit exceeded. See `Retry-After` header. */
845
+ 429: {
846
+ headers: {
847
+ [name: string]: unknown;
848
+ };
849
+ content: {
850
+ "application/problem+json": components["schemas"]["ProblemDetails"];
851
+ };
852
+ };
853
+ /** @description Server error. */
854
+ 500: {
855
+ headers: {
856
+ [name: string]: unknown;
857
+ };
858
+ content: {
859
+ "application/problem+json": components["schemas"]["ProblemDetails"];
860
+ };
861
+ };
862
+ };
863
+ };
864
+ put?: never;
865
+ post?: never;
866
+ delete?: never;
867
+ options?: never;
868
+ head?: never;
869
+ patch?: never;
870
+ trace?: never;
871
+ };
872
+ "/api/v1/questions/random": {
873
+ parameters: {
874
+ query?: never;
875
+ header?: never;
876
+ path?: never;
877
+ cookie?: never;
878
+ };
879
+ /**
880
+ * Random questions sample
881
+ * @description Up to 50 random questions in the requested language. Filter set identical to `/questions`.
882
+ */
883
+ get: {
884
+ parameters: {
885
+ query?: {
886
+ amount?: number;
887
+ /** @description Display language for category names + slug labels (subcategories/tags) */
888
+ lang?: "en" | "pl";
889
+ category?: string;
890
+ difficulty?: "easy" | "medium" | "hard";
891
+ type?: "multiple" | "boolean" | "text_input";
892
+ tags?: string;
893
+ tags_any?: string;
894
+ topic?: string;
895
+ topics_any?: string;
896
+ subcategory?: string;
897
+ quality?: "high";
898
+ regions?: string;
899
+ source?: "opentdb" | "opentriviaqa" | "mkqa" | "mmlu-prox" | "wikipedia" | "community" | "runriva" | "ai-generated" | "purchased" | "quizbase";
900
+ license?: "CC-BY-SA-4.0" | "CC-BY-SA-3.0" | "CC-BY-4.0" | "MIT" | "proprietary";
901
+ exclude?: string;
902
+ };
903
+ header?: never;
904
+ path?: never;
905
+ cookie?: never;
906
+ };
907
+ requestBody?: never;
908
+ responses: {
909
+ /** @description Random sample. */
910
+ 200: {
911
+ headers: {
912
+ [name: string]: unknown;
913
+ };
914
+ content: {
915
+ "application/json": components["schemas"]["QuestionsRandomResponse"];
916
+ };
917
+ };
918
+ /** @description Invalid query parameters. */
919
+ 400: {
920
+ headers: {
921
+ [name: string]: unknown;
922
+ };
923
+ content: {
924
+ "application/problem+json": components["schemas"]["ProblemDetails"];
925
+ };
926
+ };
927
+ /** @description Missing, malformed, invalid, or revoked API key. */
928
+ 401: {
929
+ headers: {
930
+ [name: string]: unknown;
931
+ };
932
+ content: {
933
+ "application/problem+json": components["schemas"]["ProblemDetails"];
934
+ };
935
+ };
936
+ /** @description IP not allowed (per-key allowlist). */
937
+ 403: {
938
+ headers: {
939
+ [name: string]: unknown;
940
+ };
941
+ content: {
942
+ "application/problem+json": components["schemas"]["ProblemDetails"];
943
+ };
944
+ };
945
+ /** @description Rate limit exceeded. See `Retry-After` header. */
946
+ 429: {
947
+ headers: {
948
+ [name: string]: unknown;
949
+ };
950
+ content: {
951
+ "application/problem+json": components["schemas"]["ProblemDetails"];
952
+ };
953
+ };
954
+ /** @description Server error. */
955
+ 500: {
956
+ headers: {
957
+ [name: string]: unknown;
958
+ };
959
+ content: {
960
+ "application/problem+json": components["schemas"]["ProblemDetails"];
961
+ };
962
+ };
963
+ };
964
+ };
965
+ put?: never;
966
+ post?: never;
967
+ delete?: never;
968
+ options?: never;
969
+ head?: never;
970
+ patch?: never;
971
+ trace?: never;
972
+ };
973
+ "/api/v1/questions/{id}": {
974
+ parameters: {
975
+ query?: never;
976
+ header?: never;
977
+ path?: never;
978
+ cookie?: never;
979
+ };
980
+ /**
981
+ * Single question by id
982
+ * @description Deep link / shareable URL / moderation review.
983
+ */
984
+ get: {
985
+ parameters: {
986
+ query?: {
987
+ /** @description Display language for category names + slug labels (subcategories/tags) */
988
+ lang?: "en" | "pl";
989
+ };
990
+ header?: never;
991
+ path: {
992
+ /** @description UUID v7 of the question. */
993
+ id: string;
994
+ };
995
+ cookie?: never;
996
+ };
997
+ requestBody?: never;
998
+ responses: {
999
+ /** @description Question. */
1000
+ 200: {
1001
+ headers: {
1002
+ [name: string]: unknown;
1003
+ };
1004
+ content: {
1005
+ "application/json": components["schemas"]["QuestionByIdResponse"];
1006
+ };
1007
+ };
1008
+ /** @description Missing, malformed, invalid, or revoked API key. */
1009
+ 401: {
1010
+ headers: {
1011
+ [name: string]: unknown;
1012
+ };
1013
+ content: {
1014
+ "application/problem+json": components["schemas"]["ProblemDetails"];
1015
+ };
1016
+ };
1017
+ /** @description IP not allowed (per-key allowlist). */
1018
+ 403: {
1019
+ headers: {
1020
+ [name: string]: unknown;
1021
+ };
1022
+ content: {
1023
+ "application/problem+json": components["schemas"]["ProblemDetails"];
1024
+ };
1025
+ };
1026
+ /** @description Question not found, moderated, de-duplicated, or removed upstream. */
1027
+ 404: {
1028
+ headers: {
1029
+ [name: string]: unknown;
1030
+ };
1031
+ content: {
1032
+ "application/problem+json": components["schemas"]["ProblemDetails"];
1033
+ };
1034
+ };
1035
+ /** @description Rate limit exceeded. See `Retry-After` header. */
1036
+ 429: {
1037
+ headers: {
1038
+ [name: string]: unknown;
1039
+ };
1040
+ content: {
1041
+ "application/problem+json": components["schemas"]["ProblemDetails"];
1042
+ };
1043
+ };
1044
+ /** @description Server error. */
1045
+ 500: {
1046
+ headers: {
1047
+ [name: string]: unknown;
1048
+ };
1049
+ content: {
1050
+ "application/problem+json": components["schemas"]["ProblemDetails"];
1051
+ };
1052
+ };
1053
+ };
1054
+ };
1055
+ put?: never;
1056
+ post?: never;
1057
+ delete?: never;
1058
+ options?: never;
1059
+ head?: never;
1060
+ patch?: never;
1061
+ trace?: never;
1062
+ };
1063
+ }
1064
+ interface components {
1065
+ schemas: {
1066
+ ProblemDetails: {
1067
+ /**
1068
+ * @description Absolute URL identifying the error class.
1069
+ * @example https://quizbase.runriva.com/errors/invalid_query_param
1070
+ */
1071
+ type: string;
1072
+ /** @example Invalid query parameters */
1073
+ title: string;
1074
+ /** @example 400 */
1075
+ status: number;
1076
+ /** @example lang: lang "xyz" is not supported. Supported: en, pl */
1077
+ detail: string;
1078
+ /** @example /api/v1/categories?lang=xyz */
1079
+ instance: string;
1080
+ /** @example invalid_query_param */
1081
+ code: string;
1082
+ /** @description Per-field validation errors (400 only). */
1083
+ errors?: {
1084
+ path: string;
1085
+ message: string;
1086
+ }[];
1087
+ /**
1088
+ * @description Seconds to wait before retrying (429 only).
1089
+ * @example 3600
1090
+ */
1091
+ retryAfter?: number;
1092
+ /** @description URL to upgrade plan (429 only). */
1093
+ upgradeUrl?: string;
1094
+ };
1095
+ Question: {
1096
+ /**
1097
+ * Format: uuid
1098
+ * @example 019dd2af-1e86-7991-ae22-3dc3dc5e9b2c
1099
+ */
1100
+ id: string;
1101
+ /** @example Jaka jest stolica Polski? */
1102
+ text: string;
1103
+ /** @example Warszawa */
1104
+ correctAnswer: string;
1105
+ /**
1106
+ * @example [
1107
+ * "Kraków",
1108
+ * "Gdańsk",
1109
+ * "Wrocław"
1110
+ * ]
1111
+ */
1112
+ incorrectAnswers: string[];
1113
+ /**
1114
+ * @example multiple
1115
+ * @enum {string}
1116
+ */
1117
+ type: "multiple" | "boolean" | "text_input";
1118
+ /**
1119
+ * @example easy
1120
+ * @enum {string|null}
1121
+ */
1122
+ difficulty: "easy" | "medium" | "hard" | null;
1123
+ /** @example pl */
1124
+ language: string;
1125
+ category: components["schemas"]["CategoryRef"];
1126
+ /** @description Display Layer (Plan 55) — `{slug, label}` per `?lang=`. */
1127
+ subcategories: components["schemas"]["SlugLabel"][];
1128
+ /** @description Display Layer — `{slug, label}` per `?lang=`. */
1129
+ tags: components["schemas"]["SlugLabel"][];
1130
+ /**
1131
+ * @example [
1132
+ * "pl"
1133
+ * ]
1134
+ */
1135
+ regions: string[];
1136
+ attribution: components["schemas"]["Attribution"];
1137
+ /**
1138
+ * Format: uuid
1139
+ * @description Direct parent (usually English source) when this is a translation.
1140
+ */
1141
+ translationOf: string | null;
1142
+ /**
1143
+ * Format: uuid
1144
+ * @description Canonical source across translation chains.
1145
+ */
1146
+ rootQuestionId: string | null;
1147
+ /**
1148
+ * @description Translation provenance. `null` for original-language records.
1149
+ * @enum {string|null}
1150
+ */
1151
+ translator: "machine" | "human" | "native" | null;
1152
+ /** @description Optional explanation of the correct answer. */
1153
+ explanation: string | null;
1154
+ /** @description Per-source / per-pipeline extras (enrich_meta, quizify_meta, regions_meta, rewrite_meta, distractor_validation, etc.). Shape varies; treat as forward-compatible storage. */
1155
+ extensions: {
1156
+ [key: string]: unknown;
1157
+ };
1158
+ /** @example 2026-04-28T06:03:05.191Z */
1159
+ createdAt: string;
1160
+ /** @example 2026-05-01T09:16:41.496Z */
1161
+ updatedAt: string;
1162
+ };
1163
+ CategoryRef: {
1164
+ /** @example 14 */
1165
+ id: number;
1166
+ /** @example geography */
1167
+ slug: string;
1168
+ /** @example Geography */
1169
+ name: string;
1170
+ };
1171
+ SlugLabel: {
1172
+ /** @example european-capitals */
1173
+ slug: string;
1174
+ /** @example European Capitals */
1175
+ label: string;
1176
+ };
1177
+ Attribution: {
1178
+ /**
1179
+ * @description Upstream contributor or community.
1180
+ * @example OpenTDB community
1181
+ */
1182
+ author: string | null;
1183
+ /**
1184
+ * @description Source slug.
1185
+ * @example opentdb
1186
+ */
1187
+ source: string;
1188
+ /**
1189
+ * @description Effective applied license (SPDX).
1190
+ * @example CC-BY-SA-4.0
1191
+ */
1192
+ license: string;
1193
+ /**
1194
+ * @description Original upstream license version.
1195
+ * @example 4.0
1196
+ */
1197
+ licenseVersion: string | null;
1198
+ /** @example https://creativecommons.org/licenses/by-sa/4.0/ */
1199
+ licenseUrl: string | null;
1200
+ /**
1201
+ * @description Original upstream id.
1202
+ * @example opentdb:17243
1203
+ */
1204
+ sourceId: string;
1205
+ /**
1206
+ * @description Link to upstream record.
1207
+ * @example https://opentdb.com/
1208
+ */
1209
+ url: string | null;
1210
+ /**
1211
+ * @description Our modifications per BY-SA § 3(a)(1)(B). e.g. `translated_pl`, `refined_text`, `quizified`.
1212
+ * @example [
1213
+ * "translated_pl"
1214
+ * ]
1215
+ */
1216
+ modifications: string[];
1217
+ /**
1218
+ * @description ISO timestamp of our last modification.
1219
+ * @example 2026-05-01T09:16:41.496Z
1220
+ */
1221
+ lastModified: string;
1222
+ };
1223
+ CategoriesResponse: {
1224
+ data: components["schemas"]["CategoryEntry"][];
1225
+ meta: components["schemas"]["MetaCategories"];
1226
+ };
1227
+ CategoryEntry: {
1228
+ /** @example 14 */
1229
+ id: number;
1230
+ /** @example geography */
1231
+ slug: string;
1232
+ /** @example Geografia */
1233
+ name: string;
1234
+ /** @example 22 */
1235
+ opentdbId: number | null;
1236
+ /** @example null */
1237
+ parentId: number | null;
1238
+ };
1239
+ MetaCategories: {
1240
+ /** @example 24 */
1241
+ count: number;
1242
+ /**
1243
+ * @description Language of the response content.
1244
+ * @example en
1245
+ */
1246
+ language: string;
1247
+ /**
1248
+ * Format: uuid
1249
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1250
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1251
+ */
1252
+ requestId: string;
1253
+ };
1254
+ StatsResponse: {
1255
+ /** @example 1294009 */
1256
+ total: number;
1257
+ /**
1258
+ * @example {
1259
+ * "en": 646194,
1260
+ * "pl": 647815
1261
+ * }
1262
+ */
1263
+ byLanguage: {
1264
+ [key: string]: number;
1265
+ };
1266
+ /**
1267
+ * @description Counts per source slug. Pre-launch may be empty (`{}`) — snapshot pipeline gap.
1268
+ * @example {}
1269
+ */
1270
+ bySource: {
1271
+ [key: string]: number;
1272
+ };
1273
+ byCategory: components["schemas"]["StatsCategoryEntry"][];
1274
+ byDifficulty: components["schemas"]["StatsByDifficulty"];
1275
+ byTopic: components["schemas"]["StatsTopicEntry"][];
1276
+ byTag: components["schemas"]["StatsTagEntry"][];
1277
+ meta: components["schemas"]["MetaStats"];
1278
+ };
1279
+ StatsCategoryEntry: {
1280
+ /** @example geography */
1281
+ slug: string;
1282
+ /** @example geography */
1283
+ name: string;
1284
+ /** @example 146384 */
1285
+ count: number;
1286
+ };
1287
+ /** @description Difficulty distribution. Pre-launch zero-filled — snapshot pipeline gap. */
1288
+ StatsByDifficulty: {
1289
+ /** @example 0 */
1290
+ easy: number;
1291
+ /** @example 0 */
1292
+ medium: number;
1293
+ /** @example 0 */
1294
+ hard: number;
1295
+ /** @example 0 */
1296
+ unrated: number;
1297
+ };
1298
+ StatsTopicEntry: {
1299
+ /** @example biography */
1300
+ slug: string;
1301
+ /** @example Biographies */
1302
+ label: string;
1303
+ /** @example 245103 */
1304
+ count: number;
1305
+ };
1306
+ StatsTagEntry: {
1307
+ /** @example united-states */
1308
+ slug: string;
1309
+ /** @example United States */
1310
+ label: string;
1311
+ /** @example 15281 */
1312
+ count: number;
1313
+ };
1314
+ MetaStats: {
1315
+ /** @example 2026-05-05T16:32:09.001Z */
1316
+ generatedAt: string;
1317
+ /**
1318
+ * @description Language of the response content.
1319
+ * @example en
1320
+ */
1321
+ language: string;
1322
+ /**
1323
+ * Format: uuid
1324
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1325
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1326
+ */
1327
+ requestId: string;
1328
+ };
1329
+ LanguagesResponse: {
1330
+ data: components["schemas"]["LanguageEntry"][];
1331
+ meta: components["schemas"]["MetaLanguages"];
1332
+ };
1333
+ LanguageEntry: {
1334
+ /** @example pl */
1335
+ code: string;
1336
+ /** @example Polish */
1337
+ name: string;
1338
+ /** @example Polski */
1339
+ nativeName: string;
1340
+ /** @example 647599 */
1341
+ count: number;
1342
+ };
1343
+ MetaLanguages: {
1344
+ /** @example 2 */
1345
+ count: number;
1346
+ /**
1347
+ * @description Language of the response content.
1348
+ * @example en
1349
+ */
1350
+ language: string;
1351
+ /**
1352
+ * Format: uuid
1353
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1354
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1355
+ */
1356
+ requestId: string;
1357
+ };
1358
+ TopicsListResponse: {
1359
+ data: components["schemas"]["TopicEntry"][];
1360
+ meta: components["schemas"]["MetaTopicsList"];
1361
+ _links?: components["schemas"]["Links"];
1362
+ };
1363
+ TopicEntry: {
1364
+ /** @example biography */
1365
+ slug: string;
1366
+ /**
1367
+ * @example subcategory
1368
+ * @enum {string}
1369
+ */
1370
+ kind: "tag" | "subcategory";
1371
+ /** @example Biography */
1372
+ label: string;
1373
+ /**
1374
+ * @example [
1375
+ * "biography",
1376
+ * "biographies"
1377
+ * ]
1378
+ */
1379
+ aliases: string[];
1380
+ /** @example 64977 */
1381
+ count: number;
1382
+ /** @example null */
1383
+ description: string | null;
1384
+ };
1385
+ MetaTopicsList: {
1386
+ /** @example 100 */
1387
+ count: number;
1388
+ /** @example 2184 */
1389
+ total: number;
1390
+ /**
1391
+ * @description Language of the response content.
1392
+ * @example en
1393
+ */
1394
+ language: string;
1395
+ /**
1396
+ * Format: uuid
1397
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1398
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1399
+ */
1400
+ requestId: string;
1401
+ };
1402
+ Links: {
1403
+ /** @description URL to the next page. */
1404
+ next?: string;
1405
+ /** @description URL to the previous page. */
1406
+ prev?: string;
1407
+ };
1408
+ TopicDetailResponse: {
1409
+ topic: components["schemas"]["TopicEntry"];
1410
+ facets: components["schemas"]["TopicFacets"];
1411
+ samples: components["schemas"]["TopicSampleQuestion"][];
1412
+ meta: components["schemas"]["MetaBasic"];
1413
+ };
1414
+ TopicFacets: {
1415
+ byCategory: {
1416
+ slug: string;
1417
+ name: string;
1418
+ count: number;
1419
+ }[];
1420
+ byDifficulty: {
1421
+ [key: string]: number;
1422
+ };
1423
+ byLanguage: {
1424
+ [key: string]: number;
1425
+ };
1426
+ coOccurringTags: components["schemas"]["StatsTagEntry"][];
1427
+ coOccurringSubcategories: components["schemas"]["StatsTagEntry"][];
1428
+ };
1429
+ TopicSampleQuestion: {
1430
+ /** Format: uuid */
1431
+ id: string;
1432
+ text: string;
1433
+ /** @enum {string} */
1434
+ type: "multiple" | "boolean" | "text_input";
1435
+ /** @enum {string|null} */
1436
+ difficulty: "easy" | "medium" | "hard" | null;
1437
+ language: string;
1438
+ };
1439
+ MetaBasic: {
1440
+ /**
1441
+ * @description Language of the response content.
1442
+ * @example en
1443
+ */
1444
+ language: string;
1445
+ /**
1446
+ * Format: uuid
1447
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1448
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1449
+ */
1450
+ requestId: string;
1451
+ };
1452
+ TagsListResponse: {
1453
+ data: components["schemas"]["RawSlugEntry"][];
1454
+ meta: components["schemas"]["MetaTagsList"];
1455
+ _links?: components["schemas"]["Links"];
1456
+ };
1457
+ RawSlugEntry: {
1458
+ /** @example united-states */
1459
+ slug: string;
1460
+ /** @example United States */
1461
+ label: string;
1462
+ /** @example 7673 */
1463
+ count: number;
1464
+ };
1465
+ MetaTagsList: {
1466
+ /** @example 100 */
1467
+ count: number;
1468
+ /** @example 87559 */
1469
+ total: number;
1470
+ /**
1471
+ * @description Language of the response content.
1472
+ * @example en
1473
+ */
1474
+ language: string;
1475
+ /**
1476
+ * Format: uuid
1477
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1478
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1479
+ */
1480
+ requestId: string;
1481
+ };
1482
+ SubcategoriesListResponse: {
1483
+ data: components["schemas"]["RawSlugEntry"][];
1484
+ meta: components["schemas"]["MetaTagsList"];
1485
+ _links?: components["schemas"]["Links"];
1486
+ };
1487
+ ReportAcceptedResponse: {
1488
+ /** @enum {boolean} */
1489
+ received: true;
1490
+ /**
1491
+ * Format: uuid
1492
+ * @example 019df4c5-1234-7abc-9def-0123456789ab
1493
+ */
1494
+ reportId: string;
1495
+ meta: components["schemas"]["MetaReport"];
1496
+ };
1497
+ MetaReport: {
1498
+ /**
1499
+ * Format: uuid
1500
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1501
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1502
+ */
1503
+ requestId: string;
1504
+ };
1505
+ ReportRequest: {
1506
+ /**
1507
+ * Format: uuid
1508
+ * @description UUID of the question being reported.
1509
+ */
1510
+ questionId?: string;
1511
+ /** @description Question text if no ID available. */
1512
+ questionText?: string;
1513
+ /**
1514
+ * Format: uri
1515
+ * @description Public URL where the question was seen.
1516
+ */
1517
+ questionUrl?: string;
1518
+ /**
1519
+ * @example translation
1520
+ * @enum {string}
1521
+ */
1522
+ type: "translation" | "factual" | "inappropriate" | "attribution" | "other";
1523
+ /** @description What is wrong, in your own words. */
1524
+ comment?: string;
1525
+ /**
1526
+ * Format: email
1527
+ * @description Optional follow-up address.
1528
+ */
1529
+ reporterEmail?: string;
1530
+ };
1531
+ MeResponse: {
1532
+ key: components["schemas"]["ApiKeyInfo"];
1533
+ /**
1534
+ * @example free
1535
+ * @enum {string}
1536
+ */
1537
+ tier: "free" | "indie" | "pro" | "enterprise";
1538
+ rateLimit: components["schemas"]["RateLimitInfo"];
1539
+ monthlyQuota: components["schemas"]["MonthlyQuota"];
1540
+ meta: components["schemas"]["MetaMe"];
1541
+ };
1542
+ ApiKeyInfo: {
1543
+ /** @example 01J3KXY1ZAB7CQNR8TP4Z9EXMQ */
1544
+ id: string;
1545
+ /** @example Default test key */
1546
+ name: string;
1547
+ /**
1548
+ * @description Masked display value.
1549
+ * @example qb_test_pk_••••1234
1550
+ */
1551
+ prefix: string;
1552
+ /**
1553
+ * @example test
1554
+ * @enum {string}
1555
+ */
1556
+ env: "test" | "live";
1557
+ /**
1558
+ * @example pk
1559
+ * @enum {string}
1560
+ */
1561
+ scope: "pk" | "sk";
1562
+ };
1563
+ RateLimitInfo: {
1564
+ /**
1565
+ * @description Per-minute window cap. Test keys report `Number.MAX_SAFE_INTEGER`.
1566
+ * @example 60
1567
+ */
1568
+ limit: number;
1569
+ /** @example 58 */
1570
+ remaining: number;
1571
+ /** @example 42 */
1572
+ resetInSeconds: number;
1573
+ /** @example 60 */
1574
+ perMinute: number;
1575
+ /** @example 500 */
1576
+ perDay: number;
1577
+ };
1578
+ MonthlyQuota: {
1579
+ /** @example null */
1580
+ limit: number | null;
1581
+ /** @example 0 */
1582
+ used: number;
1583
+ /** @example null */
1584
+ resetsAt: string | null;
1585
+ };
1586
+ MetaMe: {
1587
+ /**
1588
+ * Format: uuid
1589
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1590
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1591
+ */
1592
+ requestId: string;
1593
+ };
1594
+ UsageResponse: {
1595
+ summary: components["schemas"]["UsageSummary"];
1596
+ series: components["schemas"]["UsageDayPoint"][];
1597
+ meta: components["schemas"]["MetaUsage"];
1598
+ };
1599
+ UsageSummary: {
1600
+ /** @enum {string} */
1601
+ tier: "free" | "indie" | "pro" | "enterprise";
1602
+ today: components["schemas"]["UsageWindow"];
1603
+ week: components["schemas"]["UsageWindow"];
1604
+ month: components["schemas"]["UsageWindow"];
1605
+ };
1606
+ UsageWindow: {
1607
+ /** @example 142 */
1608
+ count: number;
1609
+ /** @example 87 */
1610
+ prev: number;
1611
+ /**
1612
+ * @description Percentage change vs previous equivalent window.
1613
+ * @example 63
1614
+ */
1615
+ delta: number | null;
1616
+ /** @example 500 */
1617
+ limit: number | null;
1618
+ /** @example 2026-05-04 */
1619
+ windowStart: string;
1620
+ /** @example 2026-05-04 */
1621
+ windowEnd: string;
1622
+ };
1623
+ UsageDayPoint: {
1624
+ /** @example 2026-04-28 */
1625
+ day: string;
1626
+ /** @example 87 */
1627
+ requests: number;
1628
+ /**
1629
+ * @description 5xx count (server-side).
1630
+ * @example 0
1631
+ */
1632
+ errors: number;
1633
+ /**
1634
+ * @description 4xx count (caller-side: 401/429/400/422).
1635
+ * @example 2
1636
+ */
1637
+ clientErrors: number;
1638
+ };
1639
+ MetaUsage: {
1640
+ /** @example 2026-04-28 */
1641
+ from: string;
1642
+ /** @example 2026-05-04 */
1643
+ to: string;
1644
+ /** @example 7 */
1645
+ days: number;
1646
+ /**
1647
+ * Format: uuid
1648
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1649
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1650
+ */
1651
+ requestId: string;
1652
+ };
1653
+ QuestionsListResponse: {
1654
+ data: components["schemas"]["Question"][];
1655
+ meta: components["schemas"]["MetaPaginated"];
1656
+ _links?: components["schemas"]["Links"];
1657
+ };
1658
+ MetaPaginated: {
1659
+ /**
1660
+ * @description Items on this page.
1661
+ * @example 50
1662
+ */
1663
+ count: number;
1664
+ /**
1665
+ * @description Total matching items (when `count=exact`).
1666
+ * @example 1294009
1667
+ */
1668
+ total?: number;
1669
+ /**
1670
+ * @description Planner-estimated total (default `count=estimate`).
1671
+ * @example 545379
1672
+ */
1673
+ totalEstimate?: number;
1674
+ /**
1675
+ * @description Echo of the `count` query param.
1676
+ * @example estimate
1677
+ * @enum {string}
1678
+ */
1679
+ countMode?: "estimate" | "exact" | "none";
1680
+ /**
1681
+ * @description Language of the response content.
1682
+ * @example en
1683
+ */
1684
+ language: string;
1685
+ /**
1686
+ * Format: uuid
1687
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1688
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1689
+ */
1690
+ requestId: string;
1691
+ };
1692
+ QuestionsRandomResponse: {
1693
+ data: components["schemas"]["Question"][];
1694
+ meta: components["schemas"]["MetaRandom"];
1695
+ };
1696
+ MetaRandom: {
1697
+ /** @example 5 */
1698
+ count: number;
1699
+ /**
1700
+ * @description Language of the response content.
1701
+ * @example en
1702
+ */
1703
+ language: string;
1704
+ /**
1705
+ * Format: uuid
1706
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1707
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1708
+ */
1709
+ requestId: string;
1710
+ };
1711
+ QuestionByIdResponse: {
1712
+ data: components["schemas"]["Question"];
1713
+ meta: components["schemas"]["MetaBasic"];
1714
+ };
1715
+ };
1716
+ responses: never;
1717
+ parameters: never;
1718
+ requestBodies: never;
1719
+ headers: never;
1720
+ pathItems: never;
1721
+ }
1722
+
1723
+ type Schemas = components['schemas'];
1724
+ /** Logical endpoint identifiers — stable across path-param values, used for telemetry + per-endpoint timeouts. */
1725
+ type EndpointKey = 'questions.list' | 'questions.random' | 'questions.get' | 'categories.list' | 'languages.list' | 'topics.list' | 'topics.get' | 'tags.list' | 'subcategories.list' | 'stats.get' | 'me.get' | 'usage.get' | 'report.create';
1726
+ interface ClientOptions {
1727
+ /** API key. Use `qb_test_pk_*` / `qb_test_sk_*` for development (free, unmetered) and `qb_live_*` in production. */
1728
+ apiKey: string;
1729
+ /** Override base URL. Defaults to `https://quizbase.runriva.com`. */
1730
+ baseUrl?: string;
1731
+ /** Default request timeout in ms. Defaults to 30_000. Per-endpoint overrides take precedence. */
1732
+ timeout?: number;
1733
+ /** Per-endpoint timeout overrides keyed by `EndpointKey`. */
1734
+ timeouts?: Partial<Record<EndpointKey, number>>;
1735
+ /** Number of retries for 429 / 5xx / network errors. Defaults to 2 (3 total attempts). */
1736
+ retries?: number;
1737
+ /** Optional `fetch` implementation. Defaults to global `fetch`. */
1738
+ fetch?: typeof fetch;
1739
+ /** Telemetry hook fired after every HTTP attempt (including retries). */
1740
+ onRequest?: OnRequestHook;
1741
+ /** User-Agent suffix appended to the SDK identifier. */
1742
+ userAgent?: string;
1743
+ }
1744
+ interface QuizbaseClient {
1745
+ questions: {
1746
+ list(params?: paths['/api/v1/questions']['get']['parameters']['query']): Promise<Schemas['QuestionsListResponse']>;
1747
+ random(params?: paths['/api/v1/questions/random']['get']['parameters']['query']): Promise<Schemas['QuestionsRandomResponse']>;
1748
+ get(id: string, params?: paths['/api/v1/questions/{id}']['get']['parameters']['query']): Promise<Schemas['QuestionByIdResponse']>;
1749
+ };
1750
+ categories: {
1751
+ list(params?: paths['/api/v1/categories']['get']['parameters']['query']): Promise<Schemas['CategoriesResponse']>;
1752
+ };
1753
+ languages: {
1754
+ list(): Promise<Schemas['LanguagesResponse']>;
1755
+ };
1756
+ topics: {
1757
+ list(params?: paths['/api/v1/topics']['get']['parameters']['query']): Promise<Schemas['TopicsListResponse']>;
1758
+ get(slug: string, params?: paths['/api/v1/topics/{slug}']['get']['parameters']['query']): Promise<Schemas['TopicDetailResponse']>;
1759
+ };
1760
+ tags: {
1761
+ list(params?: paths['/api/v1/tags']['get']['parameters']['query']): Promise<Schemas['TagsListResponse']>;
1762
+ };
1763
+ subcategories: {
1764
+ list(params?: paths['/api/v1/subcategories']['get']['parameters']['query']): Promise<Schemas['SubcategoriesListResponse']>;
1765
+ };
1766
+ stats: {
1767
+ get(): Promise<Schemas['StatsResponse']>;
1768
+ };
1769
+ me: {
1770
+ get(): Promise<Schemas['MeResponse']>;
1771
+ };
1772
+ usage: {
1773
+ get(params?: paths['/api/v1/usage']['get']['parameters']['query']): Promise<Schemas['UsageResponse']>;
1774
+ };
1775
+ report: {
1776
+ create(body: NonNullable<paths['/api/v1/report']['post']['requestBody']>['content']['application/json']): Promise<Schemas['ReportAcceptedResponse']>;
1777
+ };
1778
+ }
1779
+ declare function createClient(options: ClientOptions): QuizbaseClient;
1780
+
1781
+ type ProblemDetails = components['schemas']['ProblemDetails'];
1782
+ interface QuizbaseErrorOptions {
1783
+ status: number;
1784
+ problem: ProblemDetails;
1785
+ requestId: string | null;
1786
+ retryAfter: number | null;
1787
+ url: string;
1788
+ method: string;
1789
+ }
1790
+ /**
1791
+ * Thrown for any non-2xx response from the QuizBase API.
1792
+ * Carries the parsed RFC 9457 Problem Details body, X-Request-Id for support,
1793
+ * and a parsed retry-after (seconds) for 429s.
1794
+ */
1795
+ declare class QuizbaseError extends Error {
1796
+ readonly status: number;
1797
+ readonly problem: ProblemDetails;
1798
+ readonly requestId: string | null;
1799
+ readonly retryAfter: number | null;
1800
+ readonly url: string;
1801
+ readonly method: string;
1802
+ constructor(opts: QuizbaseErrorOptions);
1803
+ get type(): string | undefined;
1804
+ get isRateLimited(): boolean;
1805
+ get isAuthError(): boolean;
1806
+ get isServerError(): boolean;
1807
+ }
1808
+
1809
+ export { type ClientOptions, type EndpointKey, type OnRequestHook, type ProblemDetails, type QuizbaseClient, QuizbaseError, type QuizbaseErrorOptions, type RequestEvent, type components, createClient, type paths };