-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSCINET_API_DOC.html
More file actions
965 lines (932 loc) · 45.5 KB
/
SCINET_API_DOC.html
File metadata and controls
965 lines (932 loc) · 45.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SciNet API Documentation</title>
<style>
:root{
--bg:#f7f9fc;
--panel:#ffffff;
--ink:#162033;
--muted:#64748b;
--line:#dbe3ee;
--blue:#2563eb;
--blue-dark:#1d4ed8;
--cyan:#0891b2;
--green:#059669;
--amber:#b45309;
--rose:#be123c;
--code:#0f172a;
--shadow:0 18px 50px rgba(15,23,42,.08);
--radius:8px;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
margin:0;
background:var(--bg);
color:var(--ink);
font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
line-height:1.6;
}
a{color:var(--blue);text-decoration:none}
a:hover{text-decoration:underline}
button,input,select,textarea{font:inherit}
.topbar{
position:sticky;
top:0;
z-index:30;
display:flex;
align-items:center;
justify-content:space-between;
gap:18px;
padding:12px 28px;
border-bottom:1px solid rgba(219,227,238,.9);
background:rgba(255,255,255,.92);
backdrop-filter:saturate(180%) blur(16px);
}
.brand{display:flex;align-items:center;gap:10px;font-weight:800;color:var(--ink)}
.brand-mark{
display:grid;
place-items:center;
width:34px;
height:34px;
border-radius:8px;
background:#e0f2fe;
color:#075985;
font-weight:900;
}
.top-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end}
.pill,.lang-button{
display:inline-flex;
align-items:center;
gap:6px;
min-height:34px;
border:1px solid var(--line);
border-radius:999px;
background:#fff;
color:var(--ink);
padding:6px 11px;
font-size:13px;
font-weight:700;
}
.pill.primary{background:#eff6ff;border-color:#bfdbfe;color:#1d4ed8}
.lang-button{cursor:pointer}
.lang-button.active{background:var(--ink);border-color:var(--ink);color:#fff}
.hero{
border-bottom:1px solid var(--line);
background:
linear-gradient(135deg,rgba(37,99,235,.10),rgba(8,145,178,.08) 42%,rgba(255,255,255,0) 70%),
#fff;
}
.hero-inner{
max-width:1480px;
margin:0 auto;
padding:44px 28px 34px;
display:grid;
grid-template-columns:minmax(0,1fr) 430px;
gap:28px;
align-items:end;
}
.eyebrow{
margin:0 0 10px;
color:var(--cyan);
font-size:13px;
font-weight:900;
letter-spacing:.08em;
text-transform:uppercase;
}
h1{margin:0;font-size:44px;line-height:1.08;letter-spacing:0}
.hero-copy{max-width:820px;margin:16px 0 0;color:#475569;font-size:17px}
.hero-links{display:flex;gap:10px;flex-wrap:wrap;margin-top:22px}
.button-link{
display:inline-flex;
align-items:center;
justify-content:center;
min-height:40px;
border:1px solid var(--line);
border-radius:8px;
padding:8px 13px;
background:#fff;
color:var(--ink);
font-weight:800;
}
.button-link.primary{background:var(--blue);border-color:var(--blue);color:#fff}
.button-link:hover{text-decoration:none;border-color:#93c5fd}
.quick-panel{
border:1px solid var(--line);
border-radius:8px;
padding:18px;
background:#fff;
box-shadow:var(--shadow);
}
.quick-panel h2{margin:0 0 12px;font-size:16px}
.quick-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.metric{
border:1px solid #e6edf5;
border-radius:8px;
padding:12px;
background:#f8fafc;
}
.metric b{display:block;font-size:20px;color:var(--ink)}
.metric span{color:var(--muted);font-size:13px}
.shell{
max-width:1480px;
margin:0 auto;
padding:24px 28px 40px;
display:grid;
grid-template-columns:260px minmax(0,1fr) 360px;
gap:22px;
align-items:start;
}
.side,.playground{
position:sticky;
top:70px;
max-height:calc(100vh - 88px);
overflow:auto;
}
.panel{
border:1px solid var(--line);
border-radius:8px;
background:var(--panel);
box-shadow:0 10px 30px rgba(15,23,42,.04);
}
.side{padding:14px}
.side-title{display:block;margin:2px 0 10px;color:var(--muted);font-size:12px;font-weight:900;text-transform:uppercase;letter-spacing:.08em}
.side a{
display:flex;
align-items:center;
justify-content:space-between;
gap:8px;
border-radius:8px;
padding:8px 9px;
color:#334155;
font-size:14px;
font-weight:650;
}
.side a:hover,.side a.active{background:#eff6ff;color:#1d4ed8;text-decoration:none}
.endpoint-toc{margin-top:10px;padding-top:10px;border-top:1px solid var(--line)}
.endpoint-toc a{font-size:13px;font-weight:600}
.method-dot{font-size:11px;font-weight:900;color:var(--muted)}
.method-dot.post{color:var(--green)}
.method-dot.get{color:var(--blue)}
main{display:flex;flex-direction:column;gap:18px}
.section{
border:1px solid var(--line);
border-radius:8px;
background:#fff;
padding:24px;
}
.section h2{margin:0 0 10px;font-size:27px;letter-spacing:0}
.section h3{margin:24px 0 10px;font-size:18px}
.section p{color:#475569}
.feature-grid{
display:grid;
grid-template-columns:repeat(3,minmax(0,1fr));
gap:12px;
margin-top:18px;
}
.feature{
border:1px solid #e6edf5;
border-radius:8px;
padding:14px;
background:#fbfdff;
}
.feature b{display:block;margin-bottom:4px}
.feature p{margin:0;font-size:14px}
.callout{
border-left:4px solid var(--cyan);
border-radius:8px;
background:#ecfeff;
padding:14px 16px;
color:#155e75;
}
.toolbar{
display:flex;
align-items:center;
gap:10px;
flex-wrap:wrap;
margin-top:14px;
}
.searchbox{
flex:1;
min-width:220px;
border:1px solid var(--line);
border-radius:8px;
padding:10px 12px;
background:#fff;
}
.endpoint-card{
border:1px solid var(--line);
border-radius:8px;
background:#fff;
overflow:hidden;
}
.endpoint-card + .endpoint-card{margin-top:14px}
.endpoint-head{
display:flex;
align-items:flex-start;
justify-content:space-between;
gap:16px;
padding:18px 20px;
border-bottom:1px solid var(--line);
background:#fbfdff;
}
.endpoint-title{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.endpoint-title h3{margin:0;font-size:19px}
.badge{
display:inline-flex;
align-items:center;
min-height:25px;
border-radius:999px;
padding:3px 9px;
font-size:12px;
font-weight:900;
letter-spacing:.03em;
}
.badge.get{background:#dbeafe;color:#1d4ed8}
.badge.post{background:#dcfce7;color:#047857}
.badge.none{background:#f1f5f9;color:#475569}
.badge.required{background:#fee2e2;color:#be123c}
.path{
display:inline-flex;
align-items:center;
min-height:28px;
border:1px solid #dbeafe;
border-radius:8px;
background:#eff6ff;
color:#1e40af;
padding:2px 8px;
font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
font-size:13px;
}
.endpoint-body{padding:18px 20px}
.endpoint-desc{margin:0 0 14px;color:#475569}
.mini-actions{display:flex;gap:8px;flex-wrap:wrap}
.ghost-button{
border:1px solid var(--line);
border-radius:8px;
background:#fff;
color:#334155;
padding:7px 10px;
cursor:pointer;
font-weight:800;
font-size:13px;
}
.ghost-button:hover{border-color:#93c5fd;color:#1d4ed8}
.tabs{display:flex;gap:6px;flex-wrap:wrap;margin:12px 0}
.tab{
border:1px solid var(--line);
border-radius:999px;
background:#fff;
color:#475569;
padding:5px 10px;
cursor:pointer;
font-size:12px;
font-weight:900;
}
.tab.active{background:#0f172a;color:#fff;border-color:#0f172a}
pre{
position:relative;
margin:12px 0;
border-radius:8px;
background:var(--code);
color:#e5e7eb;
padding:16px;
overflow:auto;
line-height:1.5;
font-size:13px;
}
code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.copy-code{
position:absolute;
top:8px;
right:8px;
border:1px solid rgba(255,255,255,.18);
border-radius:8px;
background:rgba(255,255,255,.08);
color:#fff;
padding:4px 8px;
cursor:pointer;
font-size:12px;
}
table{width:100%;border-collapse:collapse;margin:12px 0 18px;font-size:14px}
th,td{border:1px solid var(--line);padding:9px 10px;text-align:left;vertical-align:top}
th{background:#f8fafc;color:#334155}
td code{color:#1d4ed8}
.playground{padding:16px}
.playground h2{margin:0 0 6px;font-size:18px}
.playground p{margin:0 0 12px;color:var(--muted);font-size:13px}
.field{display:block;margin:12px 0}
.field span{display:block;margin-bottom:6px;color:#334155;font-size:13px;font-weight:900}
.field input,.field select,.field textarea{
width:100%;
border:1px solid var(--line);
border-radius:8px;
background:#fff;
padding:9px 10px;
color:var(--ink);
}
.field textarea{min-height:150px;resize:vertical;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}
.run-button{
width:100%;
border:0;
border-radius:8px;
background:var(--blue);
color:#fff;
padding:11px 12px;
cursor:pointer;
font-weight:900;
}
.run-button:hover{background:var(--blue-dark)}
.status-line{
display:flex;
align-items:center;
justify-content:space-between;
gap:10px;
margin-top:14px;
color:var(--muted);
font-size:12px;
}
.response-box{
min-height:130px;
margin-top:8px;
border:1px solid var(--line);
border-radius:8px;
background:#0f172a;
color:#dbeafe;
padding:12px;
white-space:pre-wrap;
word-break:break-word;
font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
font-size:12px;
}
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0,0,0,0)}
@media(max-width:1240px){
.hero-inner{grid-template-columns:1fr}
.shell{grid-template-columns:220px minmax(0,1fr)}
.playground{position:static;grid-column:1 / -1;max-height:none}
}
@media(max-width:860px){
.topbar{position:static;align-items:flex-start;flex-direction:column;padding:14px 18px}
.hero-inner{padding:32px 18px 26px}
h1{font-size:34px}
.shell{display:block;padding:18px}
.side{position:static;max-height:none;margin-bottom:18px}
.playground{margin-top:18px}
.feature-grid{grid-template-columns:1fr}
.quick-grid{grid-template-columns:1fr}
.endpoint-head{display:block}
.mini-actions{margin-top:12px}
}
</style>
</head>
<body>
<div class="topbar">
<a class="brand" href="#top" aria-label="SciNet API Docs">
<span class="brand-mark">S</span>
<span>SciNet API</span>
</a>
<div class="top-actions">
<a class="pill primary" href="http://scinet.openkg.cn/register" data-i18n="getToken">Get Token</a>
<a class="pill" href="http://scinet.openkg.cn/healthz" data-i18n="healthLink">Health Check</a>
<a class="pill" href="https://github.com/zjunlp/SciNet">GitHub</a>
<button class="lang-button" type="button" data-set-lang="en">EN</button>
<button class="lang-button" type="button" data-set-lang="zh">中文</button>
</div>
</div>
<header class="hero" id="top">
<div class="hero-inner">
<div>
<p class="eyebrow" data-i18n="eyebrow">Hosted REST API</p>
<h1 data-i18n="heroTitle">SciNet API Documentation</h1>
<p class="hero-copy" data-i18n="heroCopy">Paper discovery, author retrieval, support-paper mining, and research workflows powered by a scientific knowledge graph.</p>
<div class="hero-links">
<a class="button-link primary" href="#playground" data-i18n="tryApi">Try the API</a>
<a class="button-link" href="#endpoints" data-i18n="viewEndpoints">View Endpoints</a>
</div>
</div>
<div class="quick-panel">
<h2 data-i18n="quickTitle">At a glance</h2>
<div class="quick-grid">
<div class="metric"><b>12</b><span data-i18n="metricEndpoints">documented endpoints</span></div>
<div class="metric"><b>JSON</b><span data-i18n="metricJson">agent-friendly I/O</span></div>
<div class="metric"><b>KG</b><span data-i18n="metricKg">graph-aware retrieval</span></div>
<div class="metric"><b>Token</b><span data-i18n="metricToken">personal access</span></div>
</div>
</div>
</div>
</header>
<div class="shell">
<aside class="side panel" aria-label="Documentation navigation">
<strong class="side-title" data-i18n="tocTitle">Contents</strong>
<a href="#overview" data-nav="overview"><span data-i18n="navOverview">Overview</span></a>
<a href="#auth" data-nav="auth"><span data-i18n="navAuth">Authentication</span></a>
<a href="#endpoints" data-nav="endpoints"><span data-i18n="navEndpoints">Endpoints</span></a>
<a href="#objects" data-nav="objects"><span data-i18n="navObjects">Request Objects</span></a>
<a href="#errors" data-nav="errors"><span data-i18n="navErrors">Errors & Limits</span></a>
<a href="#playground" data-nav="playground"><span data-i18n="navPlayground">Playground</span></a>
<div class="endpoint-toc" id="endpointToc"></div>
</aside>
<main>
<section class="section" id="overview">
<h2 data-i18n="overviewTitle">Overview</h2>
<p data-i18n="overviewP1">SciNet API is a hosted REST service for scientific knowledge-graph retrieval. It is designed for AI agents, research tools, command-line clients, and scientific workflow systems.</p>
<p data-i18n="overviewP2">Use it to retrieve papers from structured research plans, discover related authors, look up author publications, mine support papers, and query token status or usage.</p>
<div class="feature-grid">
<div class="feature"><b data-i18n="featureKgTitle">Graph-aware retrieval</b><p data-i18n="featureKgText">Follow keywords, citations, paper relations, and author links instead of relying on plain keyword matching.</p></div>
<div class="feature"><b data-i18n="featureAgentTitle">Agent-ready JSON</b><p data-i18n="featureAgentText">Structured request and response shapes make the API easy to call from CLI tools and AI agents.</p></div>
<div class="feature"><b data-i18n="featureUsageTitle">Personal access control</b><p data-i18n="featureUsageText">Email-verified tokens support status checks, usage tracking, and public API access.</p></div>
</div>
<h3 data-i18n="baseUrlTitle">Base URL</h3>
<pre><button class="copy-code" type="button" data-copy-code>Copy</button><code>http://scinet.openkg.cn</code></pre>
</section>
<section class="section" id="auth">
<h2 data-i18n="authTitle">Authentication</h2>
<p data-i18n="authIntro">Most endpoints require a valid SciNet API token. Use the Authorization header when possible; X-API-Key is kept for compatibility.</p>
<pre><button class="copy-code" type="button" data-copy-code>Copy</button><code>Authorization: Bearer YOUR_SCINET_TOKEN
X-API-Key: YOUR_SCINET_TOKEN</code></pre>
<div class="callout" data-i18n="authCallout">Public endpoints include health check, browser registration, sending email verification codes, and token registration.</div>
<h3 data-i18n="tokenTitle">Getting a token</h3>
<p data-i18n="tokenText">Open the registration page, complete email verification, and copy the returned scinet_xxx token. The token is shown only once, so store it securely.</p>
</section>
<section class="section" id="endpoints">
<h2 data-i18n="endpointsTitle">API Endpoints</h2>
<p data-i18n="endpointsIntro">Browse endpoint details, copy code examples, or send the selected request from the playground.</p>
<div class="toolbar">
<label class="sr-only" for="endpointSearch">Search endpoints</label>
<input class="searchbox" id="endpointSearch" type="search" data-i18n-placeholder="searchPlaceholder" placeholder="Search by endpoint, method, or description">
</div>
<div id="endpointDocs"></div>
</section>
<section class="section" id="objects">
<h2 data-i18n="objectsTitle">Common Request Objects</h2>
<p data-i18n="objectsIntro">The high-level search endpoints share a structured research plan and retrieval options.</p>
<h3 data-i18n="planTitle">Plan object</h3>
<table>
<thead><tr><th>Field</th><th>Type</th><th data-i18n="tableDesc">Description</th></tr></thead>
<tbody>
<tr><td><code>query_text</code></td><td>string</td><td data-i18n="planQuery">Natural-language topic, idea, or search intent.</td></tr>
<tr><td><code>source_type</code></td><td>string</td><td data-i18n="planSourceType">Input type, such as idea_text, paper_title, or topic.</td></tr>
<tr><td><code>keywords</code></td><td>array</td><td data-i18n="planKeywords">Weighted keyword anchors, for example high:open world agent.</td></tr>
<tr><td><code>titles</code></td><td>array</td><td data-i18n="planTitles">Known paper-title anchors.</td></tr>
<tr><td><code>reference_titles</code></td><td>array</td><td data-i18n="planReferences">Reference-paper anchors used to guide retrieval.</td></tr>
</tbody>
</table>
<h3 data-i18n="optionsTitle">Options object</h3>
<table>
<thead><tr><th>Field</th><th>Type</th><th data-i18n="tableDesc2">Description</th></tr></thead>
<tbody>
<tr><td><code>top_k</code></td><td>integer</td><td data-i18n="optionTopK">Number of main results to return.</td></tr>
<tr><td><code>retrieval_mode</code></td><td>string</td><td data-i18n="optionMode">keyword, semantic, title, or hybrid.</td></tr>
<tr><td><code>ranking_profile</code></td><td>string</td><td data-i18n="optionRanking">precision, balanced, discovery, or impact.</td></tr>
</tbody>
</table>
</section>
<section class="section" id="errors">
<h2 data-i18n="errorsTitle">Error Handling & Rate Limits</h2>
<p data-i18n="errorsIntro">Errors use regular HTTP status codes plus a short error type in the response body.</p>
<table>
<thead><tr><th>Status</th><th data-i18n="errorType">Error type</th><th data-i18n="errorMeaning">Meaning</th></tr></thead>
<tbody>
<tr><td>400</td><td><code>BadRequest</code></td><td data-i18n="err400">Invalid payload or verification code.</td></tr>
<tr><td>401</td><td><code>MissingApiKey</code> / <code>InvalidApiKey</code></td><td data-i18n="err401">Missing or invalid token.</td></tr>
<tr><td>403</td><td><code>TokenDisabled</code> / <code>TokenExpired</code></td><td data-i18n="err403">Disabled or expired token.</td></tr>
<tr><td>429</td><td><code>QuotaExceeded</code></td><td data-i18n="err429">Daily quota exhausted.</td></tr>
<tr><td>500</td><td><code>ServerError</code></td><td data-i18n="err500">Backend or email service error.</td></tr>
</tbody>
</table>
<div class="callout" data-i18n="limitsText">Each personal token has a daily quota. Heavier retrieval endpoints may consume more request units than lightweight status endpoints.</div>
</section>
</main>
<aside class="playground panel" id="playground">
<h2 data-i18n="playTitle">API Playground</h2>
<p data-i18n="playIntro">Select an endpoint, adjust the payload, copy code, or send a live request.</p>
<label class="field">
<span data-i18n="baseLabel">Base URL</span>
<input id="baseUrl" value="http://scinet.openkg.cn">
</label>
<label class="field">
<span data-i18n="endpointLabel">Endpoint</span>
<select id="endpointSelect"></select>
</label>
<label class="field">
<span data-i18n="tokenLabel">API Token</span>
<input id="apiToken" placeholder="scinet_xxx">
</label>
<label class="field">
<span data-i18n="payloadLabel">Request JSON</span>
<textarea id="requestBody"></textarea>
</label>
<button class="run-button" type="button" id="sendRequest" data-i18n="sendButton">Send Request</button>
<div class="tabs" role="tablist" aria-label="Code examples">
<button class="tab active" type="button" data-code-tab="bash">Bash</button>
<button class="tab" type="button" data-code-tab="python">Python</button>
<button class="tab" type="button" data-code-tab="javascript">JavaScript</button>
</div>
<pre><button class="copy-code" type="button" data-copy-code>Copy</button><code id="codeExample"></code></pre>
<div class="status-line">
<span id="requestStatus" data-i18n="responseIdle">Response will appear here.</span>
<button class="ghost-button" type="button" id="clearResponse" data-i18n="clearButton">Clear</button>
</div>
<div class="response-box" id="responseBox"></div>
</aside>
</div>
<script>
const I18N = {
en: {
getToken:"Get Token", healthLink:"Health Check", eyebrow:"Hosted REST API",
heroTitle:"SciNet API Documentation", heroCopy:"Paper discovery, author retrieval, support-paper mining, and research workflows powered by a scientific knowledge graph.",
tryApi:"Try the API", viewEndpoints:"View Endpoints", quickTitle:"At a glance",
metricEndpoints:"documented endpoints", metricJson:"agent-friendly I/O", metricKg:"graph-aware retrieval", metricToken:"personal access",
tocTitle:"Contents", navOverview:"Overview", navAuth:"Authentication", navEndpoints:"Endpoints", navObjects:"Request Objects", navErrors:"Errors & Limits", navPlayground:"Playground",
overviewTitle:"Overview", overviewP1:"SciNet API is a hosted REST service for scientific knowledge-graph retrieval. It is designed for AI agents, research tools, command-line clients, and scientific workflow systems.",
overviewP2:"Use it to retrieve papers from structured research plans, discover related authors, look up author publications, mine support papers, and query token status or usage.",
featureKgTitle:"Graph-aware retrieval", featureKgText:"Follow keywords, citations, paper relations, and author links instead of relying on plain keyword matching.",
featureAgentTitle:"Agent-ready JSON", featureAgentText:"Structured request and response shapes make the API easy to call from CLI tools and AI agents.",
featureUsageTitle:"Personal access control", featureUsageText:"Email-verified tokens support status checks, usage tracking, and public API access.",
baseUrlTitle:"Base URL", authTitle:"Authentication", authIntro:"Most endpoints require a valid SciNet API token. Use the Authorization header when possible; X-API-Key is kept for compatibility.",
authCallout:"Public endpoints include health check, browser registration, sending email verification codes, and token registration.",
tokenTitle:"Getting a token", tokenText:"Open the registration page, complete email verification, and copy the returned scinet_xxx token. The token is shown only once, so store it securely.",
endpointsTitle:"API Endpoints", endpointsIntro:"Browse endpoint details, copy code examples, or send the selected request from the playground.",
searchPlaceholder:"Search by endpoint, method, or description", objectsTitle:"Common Request Objects", objectsIntro:"The high-level search endpoints share a structured research plan and retrieval options.",
tableDesc:"Description", tableDesc2:"Description", planTitle:"Plan object", planQuery:"Natural-language topic, idea, or search intent.", planSourceType:"Input type, such as idea_text, paper_title, or topic.",
planKeywords:"Weighted keyword anchors, for example high:open world agent.", planTitles:"Known paper-title anchors.", planReferences:"Reference-paper anchors used to guide retrieval.",
optionsTitle:"Options object", optionTopK:"Number of main results to return.", optionMode:"keyword, semantic, title, or hybrid.", optionRanking:"precision, balanced, discovery, or impact.",
errorsTitle:"Error Handling & Rate Limits", errorsIntro:"Errors use regular HTTP status codes plus a short error type in the response body.",
errorType:"Error type", errorMeaning:"Meaning", err400:"Invalid payload or verification code.", err401:"Missing or invalid token.", err403:"Disabled or expired token.", err429:"Daily quota exhausted.", err500:"Backend or email service error.",
limitsText:"Each personal token has a daily quota. Heavier retrieval endpoints may consume more request units than lightweight status endpoints.",
playTitle:"API Playground", playIntro:"Select an endpoint, adjust the payload, copy code, or send a live request.", baseLabel:"Base URL", endpointLabel:"Endpoint", tokenLabel:"API Token", payloadLabel:"Request JSON",
sendButton:"Send Request", responseIdle:"Response will appear here.", clearButton:"Clear", auth:"Auth", noAuth:"No auth", required:"required", none:"none", fields:"Request fields", exampleBody:"Example body",
responseExample:"Example response", tryThis:"Try it", copy:"Copy", copied:"Copied", sending:"Sending request...", corsHint:"Request failed. Check network, token, CORS policy, or endpoint availability."
},
zh: {
getToken:"获取 Token", healthLink:"健康检查", eyebrow:"托管 REST API",
heroTitle:"SciNet API 文档站", heroCopy:"基于科学知识图谱的论文发现、作者检索、支撑论文挖掘和科研工作流 API。",
tryApi:"试用 API", viewEndpoints:"查看端点", quickTitle:"快速了解",
metricEndpoints:"已整理端点", metricJson:"Agent 友好输入输出", metricKg:"图谱增强检索", metricToken:"个人 Token 访问",
tocTitle:"目录", navOverview:"概览", navAuth:"认证", navEndpoints:"端点", navObjects:"请求对象", navErrors:"错误与限额", navPlayground:"调试台",
overviewTitle:"概览", overviewP1:"SciNet API 是一个托管的科学知识图谱检索 REST 服务,面向 AI Agent、科研工具、命令行客户端和科研工作流系统。",
overviewP2:"你可以用它根据结构化科研计划检索论文、发现相关作者、查询作者论文、挖掘支撑论文,并查看 Token 状态和用量。",
featureKgTitle:"图谱增强检索", featureKgText:"沿着关键词、引用、论文关系和作者关系探索,而不只是做普通关键词匹配。",
featureAgentTitle:"Agent 友好 JSON", featureAgentText:"结构化请求和响应格式,便于 CLI 工具和 AI Agent 直接调用。",
featureUsageTitle:"个人访问控制", featureUsageText:"通过邮箱验证注册 Token,并支持状态查询、用量追踪和公开 API 访问。",
baseUrlTitle:"基础地址", authTitle:"认证", authIntro:"多数端点需要有效的 SciNet API Token。推荐使用 Authorization 请求头,X-API-Key 用于兼容。",
authCallout:"公开端点包括健康检查、浏览器注册、发送邮箱验证码和注册 Token。",
tokenTitle:"获取 Token", tokenText:"打开注册页,完成邮箱验证,并复制返回的 scinet_xxx Token。Token 只显示一次,请妥善保存。",
endpointsTitle:"API 端点", endpointsIntro:"浏览端点详情、复制代码示例,或在右侧调试台直接发送请求。",
searchPlaceholder:"按端点、方法或描述搜索", objectsTitle:"通用请求对象", objectsIntro:"高层检索端点共享结构化 research plan 和 retrieval options。",
tableDesc:"说明", tableDesc2:"说明", planTitle:"Plan 对象", planQuery:"自然语言主题、idea 或检索意图。", planSourceType:"输入类型,例如 idea_text、paper_title 或 topic。",
planKeywords:"带权重的关键词锚点,例如 high:open world agent。", planTitles:"已知论文标题锚点。", planReferences:"用于引导检索的参考论文标题。",
optionsTitle:"Options 对象", optionTopK:"返回主要结果数量。", optionMode:"keyword、semantic、title 或 hybrid。", optionRanking:"precision、balanced、discovery 或 impact。",
errorsTitle:"错误处理与限额", errorsIntro:"错误使用常规 HTTP 状态码,并在响应体中返回简短错误类型。",
errorType:"错误类型", errorMeaning:"含义", err400:"请求体或验证码无效。", err401:"Token 缺失或无效。", err403:"Token 被禁用或过期。", err429:"每日配额已用尽。", err500:"后端或邮件服务错误。",
limitsText:"每个个人 Token 都有每日配额。较重的检索端点可能比轻量状态端点消耗更多请求额度。",
playTitle:"API 调试台", playIntro:"选择端点、调整请求体、复制代码,或发送实时请求。", baseLabel:"基础地址", endpointLabel:"端点", tokenLabel:"API Token", payloadLabel:"请求 JSON",
sendButton:"发送请求", responseIdle:"响应会显示在这里。", clearButton:"清空", auth:"认证", noAuth:"无需认证", required:"必需", none:"无", fields:"请求字段", exampleBody:"示例请求体",
responseExample:"示例响应", tryThis:"试用", copy:"复制", copied:"已复制", sending:"正在发送请求...", corsHint:"请求失败。请检查网络、Token、CORS 策略或端点可用性。"
}
};
const samplePlan = {
plan: {
query_text: "open world agent",
source_type: "idea_text",
source_title: null,
keywords: [{ text: "open world agent", score: 10 }],
titles: [],
reference_titles: []
},
options: {
top_k: 3,
retrieval_mode: "hybrid"
}
};
const endpoints = [
{ id:"health", method:"GET", path:"/healthz", auth:false,
title:{en:"Health Check",zh:"健康检查"},
desc:{en:"Check whether the hosted SciNet service is alive and configured.",zh:"检查托管 SciNet 服务是否可用并完成配置。"},
fields:[], body:null,
response:{status:"ok",service:"kg2api-search",configured:true}
},
{ id:"send-code", method:"POST", path:"/v1/auth/send-code", auth:false,
title:{en:"Send Email Verification Code",zh:"发送邮箱验证码"},
desc:{en:"Send a short-lived verification code to an email address before token registration.",zh:"在注册 Token 前,向邮箱发送短期有效的验证码。"},
fields:[["email","string","yes","Email address for receiving the verification code","用于接收验证码的邮箱地址"]],
body:{email:"you@example.com"},
response:{ok:true,email:"you@example.com",ttl_minutes:10,message:"Verification code sent. Please check your inbox."}
},
{ id:"register", method:"POST", path:"/v1/auth/register", auth:false,
title:{en:"Register API Token",zh:"注册 API Token"},
desc:{en:"Create a personal API token after email verification.",zh:"完成邮箱验证后创建个人 API Token。"},
fields:[["name","string","yes","User name","用户姓名"],["email","string","yes","Verified email address","已验证邮箱"],["email_code","string","yes","Email verification code","邮箱验证码"],["organization","string","optional","University, lab, company, or project","学校、实验室、公司或项目"],["use_case","string","yes","Brief description of intended usage","用途说明"]],
body:{name:"Alice Smith",email:"you@example.com",email_code:"123456",organization:"Example University",use_case:"Scientific KG retrieval experiments"},
response:{ok:true,api_key:"scinet_xxxxxxxxxxxxx",message:"Token created. Please copy it now."}
},
{ id:"token-status", method:"GET", path:"/v1/auth/token/status", auth:true,
title:{en:"Token Status",zh:"Token 状态"},
desc:{en:"Inspect whether a token is active and view basic quota metadata.",zh:"检查 Token 是否有效,并查看基础配额信息。"},
fields:[], body:null,
response:{ok:true,active:true,daily_quota:100,used_today:3}
},
{ id:"usage", method:"GET", path:"/v1/auth/usage?days=7", auth:true,
title:{en:"Usage Statistics",zh:"用量统计"},
desc:{en:"View recent request usage for the current token.",zh:"查看当前 Token 最近一段时间的请求用量。"},
fields:[["days","integer","optional","Number of recent days to aggregate","需要聚合的最近天数"]],
body:null,
response:{ok:true,days:7,total_requests:12}
},
{ id:"search", method:"POST", path:"/v1/search", auth:true,
title:{en:"Search Papers",zh:"检索论文"},
desc:{en:"Retrieve literature from a structured research plan using keyword, semantic, title, reference, and graph-aware signals.",zh:"根据结构化科研计划,结合关键词、语义、标题、参考文献和图谱信号检索论文。"},
fields:[["plan","object","yes","Structured research plan","结构化科研计划"],["options","object","optional","Retrieval options such as top_k and retrieval_mode","检索选项,如 top_k 和 retrieval_mode"]],
body:samplePlan,
response:{ok:true,items:[{title:"Example Paper",score:0.91,year:2024}],artifacts:["request.json","response.json","report.md"]}
},
{ id:"related-authors", method:"POST", path:"/v1/authors/related", auth:true,
title:{en:"Related Authors",zh:"相关作者"},
desc:{en:"Find candidate authors related to a research topic or plan.",zh:"根据研究主题或计划发现相关候选作者。"},
fields:[["plan","object","yes","Structured research plan","结构化科研计划"],["top_k","integer","optional","Number of authors to return","返回作者数量"]],
body:{plan:samplePlan.plan,top_k:5},
response:{ok:true,authors:[{name:"Example Author",score:0.84,works_count:42}]}
},
{ id:"author-papers", method:"POST", path:"/v1/authors/papers", auth:true,
title:{en:"Author Papers",zh:"作者论文"},
desc:{en:"Fetch papers by author name or author ID.",zh:"根据作者姓名或作者 ID 查询论文。"},
fields:[["author","string","yes","Author name or ID","作者姓名或 ID"],["limit","integer","optional","Maximum number of papers","最大论文数量"]],
body:{author:"Yoshua Bengio",limit:10,no_abstract:true},
response:{ok:true,papers:[{title:"Example Author Paper",year:2023,cited_by_count:120}]}
},
{ id:"support-papers", method:"POST", path:"/v1/authors/support-papers", auth:true,
title:{en:"Support Papers",zh:"支撑论文"},
desc:{en:"Retrieve evidence papers for candidate authors and a topic plan.",zh:"为候选作者和研究主题检索支撑证据论文。"},
fields:[["plan","object","yes","Structured research plan","结构化科研计划"],["authors","array","yes","Candidate author names or IDs","候选作者姓名或 ID"]],
body:{plan:samplePlan.plan,authors:["Example Author"],top_k:3},
response:{ok:true,support_papers:[{author:"Example Author",title:"Evidence Paper",score:0.78}]}
},
{ id:"paper-search", method:"POST", path:"/v1/papers/search", auth:true,
title:{en:"Low-level Paper Search",zh:"底层论文搜索"},
desc:{en:"Run lightweight paper search with query and optional filters.",zh:"使用查询词和可选过滤条件执行轻量论文搜索。"},
fields:[["query","string","yes","Search query","检索查询"],["limit","integer","optional","Maximum result count","最大结果数"]],
body:{query:"retrieval augmented generation",limit:5},
response:{ok:true,papers:[{title:"Example Search Result",score:0.72}]}
},
{ id:"title-resolve", method:"POST", path:"/v1/papers/title-resolve", auth:true,
title:{en:"Title Resolve",zh:"标题解析"},
desc:{en:"Resolve paper titles into canonical paper records.",zh:"将论文标题解析为规范论文记录。"},
fields:[["titles","array","yes","Paper titles to resolve","需要解析的论文标题"]],
body:{titles:["Voyager: An Open-Ended Embodied Agent with Large Language Models"]},
response:{ok:true,matches:[{title:"Voyager: An Open-Ended Embodied Agent with Large Language Models",confidence:0.96}]}
},
{ id:"keyword-search", method:"POST", path:"/v1/keywords/search", auth:true,
title:{en:"Keyword Search",zh:"关键词搜索"},
desc:{en:"Search normalized keyword nodes used by the knowledge graph.",zh:"搜索知识图谱中的规范化关键词节点。"},
fields:[["query","string","yes","Keyword query","关键词查询"],["limit","integer","optional","Maximum result count","最大结果数"]],
body:{query:"knowledge graph",limit:10},
response:{ok:true,keywords:[{text:"knowledge graph",frequency:12345}]}
}
];
let lang = new URLSearchParams(location.search).get("lang") || (location.hash.includes("lang=zh") ? "zh" : "en");
if (!I18N[lang]) lang = "en";
let currentEndpointId = endpoints[0].id;
let currentCodeTab = "bash";
const $ = (selector, root=document) => root.querySelector(selector);
const $$ = (selector, root=document) => Array.from(root.querySelectorAll(selector));
const t = (key) => I18N[lang][key] || I18N.en[key] || key;
const textFor = (value) => typeof value === "object" ? value[lang] || value.en : value;
const pretty = (value) => value === null || value === undefined || value === "" ? "" : JSON.stringify(value, null, 2);
const escapeHtml = (value) => String(value ?? "").replace(/[&<>"']/g, (char) => ({ "&":"&", "<":"<", ">":">", '"':""", "'":"'" }[char]));
const endpointById = (id) => endpoints.find((endpoint) => endpoint.id === id) || endpoints[0];
function applyTranslations(){
document.documentElement.lang = lang === "zh" ? "zh-CN" : "en";
document.title = t("heroTitle");
$$("[data-i18n]").forEach((node) => { node.textContent = t(node.dataset.i18n); });
$$("[data-i18n-placeholder]").forEach((node) => { node.placeholder = t(node.dataset.i18nPlaceholder); });
$$("[data-set-lang]").forEach((button) => button.classList.toggle("active", button.dataset.setLang === lang));
}
function renderEndpointToc(){
$("#endpointToc").innerHTML = `<strong class="side-title">${escapeHtml(t("navEndpoints"))}</strong>` + endpoints.map((endpoint) => `
<a href="#endpoint-${endpoint.id}" data-endpoint-link="${endpoint.id}">
<span>${escapeHtml(textFor(endpoint.title))}</span>
<span class="method-dot ${endpoint.method.toLowerCase()}">${endpoint.method}</span>
</a>
`).join("");
}
function fieldsTable(endpoint){
if (!endpoint.fields.length) return "";
return `
<h3>${escapeHtml(t("fields"))}</h3>
<table>
<thead><tr><th>Field</th><th>Type</th><th>${escapeHtml(t("required"))}</th><th>${escapeHtml(t("tableDesc"))}</th></tr></thead>
<tbody>${endpoint.fields.map((field) => `
<tr><td><code>${escapeHtml(field[0])}</code></td><td>${escapeHtml(field[1])}</td><td>${escapeHtml(field[2])}</td><td>${escapeHtml(lang === "zh" ? field[4] : field[3])}</td></tr>
`).join("")}</tbody>
</table>
`;
}
function codeBlock(content){
return `<pre><button class="copy-code" type="button" data-copy-code>${escapeHtml(t("copy"))}</button><code>${escapeHtml(content)}</code></pre>`;
}
function buildCurl(endpoint){
const base = $("#baseUrl")?.value || "http://scinet.openkg.cn";
const headers = endpoint.auth ? ` \\\n -H "Authorization: Bearer $SCINET_API_KEY"` : "";
if (endpoint.method === "GET") return `curl -sS${headers} "${base}${endpoint.path}"`;
return `curl -sS \\
-X ${endpoint.method}${headers} \\
-H "Content-Type: application/json" \\
"${base}${endpoint.path}" \\
-d '${JSON.stringify(endpoint.body)}'`;
}
function buildPython(endpoint){
const base = $("#baseUrl")?.value || "http://scinet.openkg.cn";
const payload = pretty(endpoint.body);
return `import json
import requests
base_url = "${base}"
headers = ${endpoint.auth ? '{"Authorization": "Bearer YOUR_SCINET_TOKEN"}' : '{}'}
${endpoint.method === "GET" ? `response = requests.get(f"{base_url}${endpoint.path}", headers=headers)` : `payload = json.loads(r'''${payload}''')
response = requests.${endpoint.method.toLowerCase()}(f"{base_url}${endpoint.path}", headers=headers, json=payload)`}
print(response.status_code)
print(response.json())`;
}
function buildJavaScript(endpoint){
const base = $("#baseUrl")?.value || "http://scinet.openkg.cn";
const headers = endpoint.auth ? `{"Authorization": "Bearer YOUR_SCINET_TOKEN"` : `{`;
const contentHeader = endpoint.method === "GET" ? "}" : `${endpoint.auth ? "," : ""} "Content-Type": "application/json"}`;
const body = endpoint.method === "GET" ? "" : `,\n body: JSON.stringify(${pretty(endpoint.body).replace(/\n/g, "\n ")})`;
return `const response = await fetch("${base}${endpoint.path}", {
method: "${endpoint.method}",
headers: ${headers}${contentHeader}${body}
});
console.log(await response.json());`;
}
function renderEndpointDocs(){
$("#endpointDocs").innerHTML = endpoints.map((endpoint) => `
<article class="endpoint-card" id="endpoint-${endpoint.id}" data-endpoint-card="${endpoint.id}">
<div class="endpoint-head">
<div>
<div class="endpoint-title">
<span class="badge ${endpoint.method.toLowerCase()}">${endpoint.method}</span>
<h3>${escapeHtml(textFor(endpoint.title))}</h3>
<span class="path">${escapeHtml(endpoint.path)}</span>
</div>
<p class="endpoint-desc">${escapeHtml(textFor(endpoint.desc))}</p>
<span class="badge ${endpoint.auth ? "required" : "none"}">${escapeHtml(endpoint.auth ? t("auth") : t("noAuth"))}</span>
</div>
<div class="mini-actions">
<button class="ghost-button" type="button" data-try-endpoint="${endpoint.id}">${escapeHtml(t("tryThis"))}</button>
<button class="ghost-button" type="button" data-copy-endpoint="${endpoint.id}">${escapeHtml(t("copy"))}</button>
</div>
</div>
<div class="endpoint-body">
${fieldsTable(endpoint)}
${endpoint.body ? `<h3>${escapeHtml(t("exampleBody"))}</h3>${codeBlock(pretty(endpoint.body))}` : ""}
<h3>${escapeHtml(t("responseExample"))}</h3>
${codeBlock(pretty(endpoint.response))}
</div>
</article>
`).join("");
}
function renderSelect(){
$("#endpointSelect").innerHTML = endpoints.map((endpoint) => `
<option value="${endpoint.id}">${endpoint.method} ${endpoint.path} - ${escapeHtml(textFor(endpoint.title))}</option>
`).join("");
}
function updatePlayground(endpointId=currentEndpointId){
currentEndpointId = endpointId;
const endpoint = endpointById(endpointId);
$("#endpointSelect").value = endpoint.id;
$("#requestBody").value = endpoint.body ? pretty(endpoint.body) : "";
updateCodeExample();
}
function updateCodeExample(){
const endpoint = endpointById(currentEndpointId);
const builders = { bash:buildCurl, python:buildPython, javascript:buildJavaScript };
$("#codeExample").textContent = builders[currentCodeTab](endpoint);
}
function filterEndpoints(query){
const needle = query.trim().toLowerCase();
endpoints.forEach((endpoint) => {
const haystack = `${endpoint.method} ${endpoint.path} ${textFor(endpoint.title)} ${textFor(endpoint.desc)}`.toLowerCase();
const visible = !needle || haystack.includes(needle);
$(`[data-endpoint-card="${endpoint.id}"]`)?.toggleAttribute("hidden", !visible);
$(`[data-endpoint-link="${endpoint.id}"]`)?.toggleAttribute("hidden", !visible);
});
}
async function copyText(text, button){
try {
await navigator.clipboard.writeText(text);
if (button) {
const original = button.textContent;
button.textContent = t("copied");
setTimeout(() => { button.textContent = original; }, 1200);
}
} catch {
const textarea = document.createElement("textarea");
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
textarea.remove();
}
}
async function sendRequest(){
const endpoint = endpointById(currentEndpointId);
const base = $("#baseUrl").value.replace(/\/$/, "");
const token = $("#apiToken").value.trim();
const status = $("#requestStatus");
const responseBox = $("#responseBox");
status.textContent = t("sending");
responseBox.textContent = "";
const headers = {};
if (endpoint.auth && token) headers.Authorization = `Bearer ${token}`;
const options = { method:endpoint.method, headers };
if (endpoint.method !== "GET") {
headers["Content-Type"] = "application/json";
const raw = $("#requestBody").value.trim();
if (raw) options.body = raw;
}
try {
const started = performance.now();
const response = await fetch(`${base}${endpoint.path}`, options);
const text = await response.text();
const elapsed = Math.round(performance.now() - started);
status.textContent = `${response.status} ${response.statusText} · ${elapsed}ms`;
try {
responseBox.textContent = JSON.stringify(JSON.parse(text), null, 2);
} catch {
responseBox.textContent = text || "(empty response)";
}
} catch (error) {
status.textContent = t("corsHint");
responseBox.textContent = String(error);
}
}
function bindEvents(){
$$("[data-set-lang]").forEach((button) => {
button.addEventListener("click", () => {
lang = button.dataset.setLang;
history.replaceState(null, "", lang === "zh" ? "?lang=zh" : location.pathname);
boot(false);
});
});
$("#endpointSearch").addEventListener("input", (event) => filterEndpoints(event.target.value));
$("#endpointSelect").addEventListener("change", (event) => updatePlayground(event.target.value));
$("#baseUrl").addEventListener("input", updateCodeExample);
$("#sendRequest").addEventListener("click", sendRequest);
$("#clearResponse").addEventListener("click", () => {
$("#responseBox").textContent = "";
$("#requestStatus").textContent = t("responseIdle");
});
$$(".tab").forEach((tab) => {
tab.addEventListener("click", () => {
currentCodeTab = tab.dataset.codeTab;
$$(".tab").forEach((other) => other.classList.toggle("active", other === tab));
updateCodeExample();
});
});
document.addEventListener("click", (event) => {
const copyButton = event.target.closest("[data-copy-code]");
if (copyButton) {
const code = copyButton.parentElement.querySelector("code")?.textContent || "";
copyText(code, copyButton);
}
const tryButton = event.target.closest("[data-try-endpoint]");
if (tryButton) {
updatePlayground(tryButton.dataset.tryEndpoint);
$("#playground").scrollIntoView({ behavior:"smooth", block:"start" });
}
const copyEndpoint = event.target.closest("[data-copy-endpoint]");
if (copyEndpoint) {
copyText(buildCurl(endpointById(copyEndpoint.dataset.copyEndpoint)), copyEndpoint);
}
});
}
function observeSections(){
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
$$("[data-nav]").forEach((link) => link.classList.toggle("active", link.getAttribute("href") === `#${entry.target.id}`));
const cardId = entry.target.dataset.endpointCard;
if (cardId) {
$$("[data-endpoint-link]").forEach((link) => link.classList.toggle("active", link.dataset.endpointLink === cardId));
}
});
}, { rootMargin:"-20% 0px -65% 0px", threshold:0.01 });
$$(".section, .endpoint-card").forEach((section) => observer.observe(section));
}
function boot(withBindings=true){
applyTranslations();
renderEndpointToc();
renderEndpointDocs();
renderSelect();
updatePlayground(currentEndpointId);
if (withBindings) {
bindEvents();
observeSections();
}
}
boot();
</script>
</body>
</html>