KERNIT Documentation
/hyperlink-applyPOST /hyperlink-apply
/hyperlink-apply writes the approved links into the DOCX. It verifies that the file, settings, scan token, and review graph still match before returning final output.
/hyperlink-apply
Apply reviewed links to a DOCX#
Writes approved citation, cross-reference, DOI, URL, and resolver decisions into the same DOCX used during scan. The route verifies scanToken, scanFingerprint, file hash, settings hash, persisted metadata hash, caller, and graph state before returning final output.
Contract Source#
This reference is backed by /docs/openapi.json. The source schema lives in app/docs/openapi.json, so request fields, response schemas, examples, and validation stay tied to one API contract.
Request Fields#
Defined by the OpenAPI contract requestBody multipart/form-data.
| Field | Type | Required | Description |
|---|---|---|---|
file | string:binary | Yes | Same DOCX package used during scan. |
settings | string | Yes | JSON string matching the settings used during scan. |
scanToken | string | Yes | Signed token returned by scan or resolver. |
scanFingerprint | string | Yes | Fingerprint returned by scan or resolver. |
decisions | array<ReviewDecision> | Yes | JSON array of ReviewDecision objects. |
resolvedWorks | string | No | Optional JSON resolver evidence to apply when approved. |
cslFile | string:binary | No | Optional CSL XML file for controlled bibliography rewrite. |
detectedCslId | string | No | Optional detected CSL style id. |
applyBibRewrite | true | false | No | Set true only when bibliography rewrite is explicitly approved. |
Example Request#
curl -X POST https://api.kernit.org/hyperlink-apply \
-H "Authorization: Bearer sk_kernit_live_YOUR_KEY" \
-F "file=@paper.docx" \
-F 'settings={"refs":true,"figures":"number","tables":"number","equations":"number","sections":"number","doi":true}' \
-F "scanToken=..." \
-F "scanFingerprint=..." \
-F 'decisions=[{"refId":"r1","status":"approved"}]' \
-F 'resolvedWorks={"r1":{"doi":"10.1234/example","confidence":0.94}}' \
-F "cslFile=@paper.docx" \
-F "detectedCslId=..." \
-F "applyBibRewrite=..."Code Examples#
curl -X POST https://api.kernit.org/hyperlink-apply \
-H "Authorization: Bearer sk_kernit_live_YOUR_KEY" \
-F "file=@paper.docx" \
-F 'settings={"refs":true,"figures":"number","tables":"number","equations":"number","sections":"number","doi":true}' \
-F "scanToken=..." \
-F "scanFingerprint=..." \
-F 'decisions=[{"refId":"r1","status":"approved"}]' \
-F 'resolvedWorks={"r1":{"doi":"10.1234/example","confidence":0.94}}' \
-F "cslFile=@paper.docx" \
-F "detectedCslId=..." \
-F "applyBibRewrite=..."const apiKey = 'sk_kernit_live_YOUR_KEY';
const form = new FormData();
form.append('file', fileInput.files[0]);
form.append('settings', "{\"refs\":true,\"figures\":\"number\",\"tables\":\"number\",\"equations\":\"number\",\"sections\":\"number\",\"doi\":true,\"crossref\":true}");
form.append('scanToken', "eyJ...");
form.append('scanFingerprint', "sha256:...");
form.append('decisions', "[{\"refId\":\"r1\",\"status\":\"approved\"}]");
form.append('resolvedWorks', "{\"r1\":{\"doi\":\"10.1234/example\",\"confidence\":0.94}}");
form.append('cslFile', fileInput.files[0]);
form.append('detectedCslId', "...");
form.append('applyBibRewrite', "...");
const response = await fetch('https://api.kernit.org/hyperlink-apply', {
method: 'POST',
headers: { Authorization: 'Bearer ' + apiKey },
body: form
});
const text = await response.text();import requests
api_key = 'sk_kernit_live_YOUR_KEY'
with open('paper.docx', 'rb') as docx:
response = requests.post(
'https://api.kernit.org/hyperlink-apply',
headers={'Authorization': f'Bearer {api_key}'},
files={'file': ('paper.docx', docx, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')},
data={
'settings': "{\"refs\":true,\"figures\":\"number\",\"tables\":\"number\",\"equations\":\"number\",\"sections\":\"number\",\"doi\":true,\"crossref\":true}",
'scanToken': "eyJ...",
'scanFingerprint': "sha256:...",
'decisions': "[{\"refId\":\"r1\",\"status\":\"approved\"}]",
'resolvedWorks': "{\"r1\":{\"doi\":\"10.1234/example\",\"confidence\":0.94}}",
'detectedCslId': "...",
'applyBibRewrite': "...",
},
timeout=480
)
print(response.status_code)
print(response.text)Try This Endpoint#
Responses#
| Status | Meaning | Schema |
|---|---|---|
200 | Base64 DOCX and apply statistics. | ApplyResult |
400 | Invalid request shape, missing file, invalid JSON, or rejected DOCX. | ErrorResponse |
401 | Scan token missing, invalid, or not aligned with file/settings/graph/caller state. | ErrorResponse |
402 | No credits or plan allowance available. | ErrorResponse |
413 | DOCX exceeds the current 25 MB limit. | ErrorResponse |
default | Unexpected KERNIT-side service error. | ErrorResponse |
Example Success Body#
{
"docx": "UEsDBBQAAAA...",
"linkCount": 47,
"statCounts": {
"cite": 23,
"fig": 6,
"tbl": 8
},
"appliedDecisions": 4,
"unmatched": []
}Error Examples#
{
"error": "Invalid JSON in form data"
}{
"error": "scan token invalid",
"reason": "missing"
}{
"error": "No credits remaining"
}{
"error": "File exceeds 25MB limit"
}Implementation Edge Cases#
| Case | Expected handling |
|---|---|
| File mismatch | Return scan-token validation failure. Apply must receive the same DOCX package used during scan. |
| Settings mismatch | Return scan-token validation failure. Store settings with the review session and submit them unchanged. |
| Missing or invalid decisions | Return 400. Decisions must use approved, skipped, retargeted, or link_only statuses from the ReviewDecision schema. |
| No apply allowance | Return 402. Route the user to billing, credits, or team allowance rather than retrying the same request. |