Appearance
Language Check
A language check assesses the writing quality of a manuscript and returns a scored report. This page is the integration contract for the language check: the event ChronosHub publishes when a manuscript is ready, and the events the partner publishes back as the check progresses.
Unlike the scope check, this is asynchronous. There is no request/response pair: messages flow over the ChronosHub Integration Framework in both directions, and a single check produces one outbound event and one or more inbound ones. All messages are JSON. The schemas below are JSON Schema (draft 2020-12).
This contract describes the check payloads only — not the transport-level concerns (topic provisioning, authentication) which are agreed per partner during onboarding.
Envelope
Every message is wrapped in an Integration Framework envelope. payload is a JSON-encoded string, not a nested object — the payload schemas in the rest of this page describe the document inside that string.
Outbound (ChronosHub → Integration Framework → partner):
json
{
"eventId": "0c5a6bd7-77b5-5c3e-9c0e-1f8b0f5a2e11",
"eventType": "manuscript.uploaded",
"partnerName": "Enago",
"payload": "{\"correlationId\":\"lc_01J...\",\"fileUrl\":\"https://...\"}"
}Inbound (partner → Integration Framework → ChronosHub):
json
{
"eventId": "3f1d1f1e-9a4b-4c2f-8a1e-6d5c4b3a2f10",
"partnerId": "b1e2d3c4-5a6b-7c8d-9e0f-1a2b3c4d5e6f",
"eventType": "language.report.status.updated",
"eventContractVersion": 1,
"payload": "{\"correlationId\":\"lc_01J...\",\"status\":\"PROCESSING\", ...}",
"receivedAt": "2026-08-07T12:37:28.844Z"
}correlationId inside every payload is the ChronosHub language check id. It is the only thing tying the exchange together and must be echoed back unchanged on every inbound event.
Outbound: manuscript.uploaded
Published when a manuscript has been uploaded and is ready to check.
json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "ManuscriptUploadedPayload",
"type": "object",
"additionalProperties": false,
"required": ["correlationId", "fileUrl"],
"properties": {
"correlationId": {
"type": "string",
"description": "ChronosHub language check id. Echo this back on every inbound event.",
"minLength": 1
},
"fileUrl": {
"type": "string",
"format": "uri",
"description": "Time-limited, IP-restricted download link for the manuscript. Accepted formats are .docx, .pdf and .tex."
}
}
}Example
json
{
"correlationId": "lc_01JQ8Z3M6T7B9C0D1E2F3G4H5J",
"fileUrl": "https://stchronosmcprod.blob.core.windows.net/manuscripts/lc_01JQ8Z.../manuscript.docx?sv=..."
}eventId is a deterministic UUIDv5 derived from the correlation id, so an at-least-once redelivery carries the same eventId. Partners should de-duplicate on it rather than starting a second check.
Inbound: language.report.status.updated
Progress signals while the check is running. Sent zero or more times.
json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "ReportStatusPayload",
"type": "object",
"required": ["correlationId", "status", "reportId"],
"properties": {
"correlationId": { "type": "string", "minLength": 1 },
"status": {
"type": "string",
"description": "Progress state. A successful outcome is not reported here — it arrives as language.report.generated.",
"enum": ["RECEIVED", "PROCESSING", "FAILED"]
},
"message": {
"type": ["string", "null"],
"description": "Human-readable detail, typically the reason on FAILED."
},
"reportId": {
"type": "string",
"description": "Partner identifier for the report."
},
"processStartTime": {
"type": ["string", "null"],
"format": "date-time",
"description": "Null until processing begins."
},
"processEndTime": {
"type": ["string", "null"],
"format": "date-time",
"description": "Null unless the check has finished."
}
}
}Example
json
{
"status": "PROCESSING",
"message": null,
"reportId": "d3ba4535-9036-4192-b760-2ae44d56c484",
"correlationId": "lc_01JQ8Z3M6T7B9C0D1E2F3G4H5J",
"processEndTime": null,
"processStartTime": "2026-08-07T12:37:28.844Z"
}Inbound: language.report.generated
The terminal success event, carrying the finished report.
json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "ReportGeneratedPayload",
"type": "object",
"required": [
"correlationId", "reportId", "reportLink", "token",
"processStartTime", "processEndTime", "data"
],
"properties": {
"correlationId": { "type": "string", "minLength": 1 },
"reportId": { "type": "string" },
"reportLink": {
"type": "string",
"format": "uri",
"description": "Link to the hosted report."
},
"token": {
"type": "string",
"description": "Access token for the report link. May be an empty string when the link needs none."
},
"processStartTime": { "type": "string", "format": "date-time" },
"processEndTime": { "type": "string", "format": "date-time" },
"data": {
"type": "object",
"required": ["totals", "summary"],
"properties": {
"totals": { "$ref": "#/$defs/documentCounts" },
"summary": {
"type": "object",
"required": ["score", "alertCounts", "globalCounts", "recommendation"],
"properties": {
"score": {
"type": "object",
"required": ["finalScore", "finalScoreBifurcation"],
"properties": {
"finalScore": {
"type": "number",
"description": "Overall language score."
},
"finalScoreBifurcation": {
"type": "object",
"description": "Per-dimension breakdown, keyed by dimension name.",
"additionalProperties": { "type": "number" }
}
}
},
"alertCounts": {
"type": "object",
"description": "Alert totals keyed by category name.",
"additionalProperties": { "$ref": "#/$defs/alertCategory" }
},
"globalCounts": { "$ref": "#/$defs/documentCounts" },
"recommendation": {
"type": "object",
"required": ["service", "description", "website"],
"properties": {
"service": { "type": "string" },
"description": { "type": "string" },
"website": { "type": "string", "format": "uri" }
}
}
}
}
}
}
},
"$defs": {
"documentCounts": {
"type": "object",
"required": [
"totalParagraphCount", "totalSentenceCount", "totalAlerts", "totalCriticalAlerts",
"totalWordsCount", "totalLongParagraphCount", "totalLongSentenceCount"
],
"properties": {
"totalParagraphCount": { "type": "integer" },
"totalSentenceCount": { "type": "integer" },
"totalAlerts": { "type": "integer" },
"totalCriticalAlerts": { "type": "integer" },
"totalWordsCount": { "type": "integer" },
"totalLongParagraphCount": { "type": "integer" },
"totalLongSentenceCount": { "type": "integer" }
}
},
"alertCategory": {
"type": "object",
"required": ["total", "categories"],
"properties": {
"total": { "type": "integer" },
"categories": {
"type": "object",
"description": "Shape varies per category — flat counts for some, nested groupings for others. Stored verbatim by ChronosHub and left open by this contract."
}
}
}
}
}Example
json
{
"data": {
"totals": {
"totalAlerts": 53,
"totalWordsCount": 758,
"totalSentenceCount": 58,
"totalCriticalAlerts": 22,
"totalParagraphCount": 31,
"totalLongSentenceCount": 0,
"totalLongParagraphCount": 0
},
"summary": {
"score": {
"finalScore": 54.310344827586206,
"finalScoreBifurcation": {
"conciseness": 100,
"correctness": 8.62068965517241,
"formal_tone": 100,
"readability": 100,
"inclusive_language": 100
}
},
"alertCounts": {
"correctness": {
"total": 53,
"categories": {
"Grammar": { "tense": 2, "verbs": 1, "articles": 6 },
"Punctuation": { "punctuation": 6 }
}
},
"consistency": { "total": 0, "categories": {} }
},
"globalCounts": {
"totalAlerts": 53,
"totalWordsCount": 758,
"totalSentenceCount": 58,
"totalCriticalAlerts": 22,
"totalParagraphCount": 31,
"totalLongSentenceCount": 0,
"totalLongParagraphCount": 0
},
"recommendation": {
"service": "Substantive Editing",
"website": "https://www.enago.com/pub/oup/substantive-editing.htm",
"description": "Your document requires advanced language check."
}
}
},
"token": "",
"reportId": "d3ba4535-9036-4192-b760-2ae44d56c484",
"reportLink": "https://reports.example.com/report/9d42be0d/abc123",
"correlationId": "lc_01JQ8Z3M6T7B9C0D1E2F3G4H5J",
"processEndTime": "2026-08-07T12:37:41.876Z",
"processStartTime": "2026-08-07T12:37:28.844Z"
}Notes
- All property names are case-sensitive. Both directions use
camelCase— unlike the scope check, whose response issnake_case. - A successful outcome never arrives as a status update.
statuscarries onlyRECEIVED,PROCESSINGandFAILED; success islanguage.report.generated. An unrecognised status value is rejected rather than ignored, so it dead-letters instead of silently dropping a signal. - Status updates need not arrive in order, and may be skipped. ChronosHub advances a check monotonically, so a late
RECEIVEDafter aPROCESSINGis discarded rather than regressing it. - Extra properties are tolerated. The report-generated payload observed in practice also carries a top-level
statusofSUCCESS, which ChronosHub ignores; the event type already conveys it. fileUrlis time-limited and pinned to the partner's egress IP, so it must be fetched from the agreed source address and before it expires.- A partner failure, a malformed payload, or an inbound event whose
correlationIdmatches no known check causes the check to be recorded as failed (or ignored, for an unknown id) on the ChronosHub side; it does not corrupt the manuscript's record.