SheetDiff™Structural Diff API

Structural Diff API

A REST API that compares an AI-generated transcript against its annotator post-edit — detecting row-level structural changes (splits, merges, modifications, additions, deletions), with per-column diff detail, CER/WER/SER scoring, and a composite quality grade per batch.

API Key Auth
x-api-key header
Rate Limited
10/min · 60/15min
JSON REST
application/json
8-Pass Engine
split + merge detection

Quick Start

No SDK needed. Send a POST request with your two arrays of transcript rows and receive a full diff in JSON. The API is in tasting phase — request an API key to get started.

1. Verify the service is live:

bash
curl https://structural-diff-engine.onrender.com/v1/health

2. Run a comparison:

curl -X POST https://structural-diff-engine.onrender.com/v1/diff \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "original": [
      { "speaker": "Alice", "start_time": 0, "end_time": 1, "transcript": "Hello world" },
      { "speaker": "Bob",   "start_time": 1, "end_time": 3, "transcript": "Good morning everyone" }
    ],
    "reworked": [
      { "speaker": "Alice", "start_time": 0, "end_time": 1, "transcript": "Hello there" },
      { "speaker": "Bob",   "start_time": 1, "end_time": 2, "transcript": "Good morning" },
      { "speaker": "Bob",   "start_time": 2, "end_time": 3, "transcript": "everyone" }
    ]
  }'

Base URL

All endpoints are prefixed with /v1.

url
https://structural-diff-engine.onrender.com

Authentication

Include your API key in the x-api-key request header on every call to /v1/diff.

bash
curl -H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -X POST https://structural-diff-engine.onrender.com/v1/diff -d '{...}'
Keys are provisioned individually per agency. A missing or invalid key returns 401 Unauthorized.

Rate Limits

Two independent tiers are enforced per API key, falling back to IP when no key is present. Exceeding either tier returns 429 Too Many Requests.

TierLimitResponse header
Burst10 requests / minuteRateLimit-Limit
Window60 requests / 15 minutesRateLimit-Remaining

Endpoints

GET /v1/health

Lightweight liveness probe. No authentication required. Returns service version and uptime.

GET/v1/health· No auth
json
{ "status": "ok", "version": "1.0.0", "uptime": 42, "timestamp": "..." }

POST /v1/diff

Compare two arrays of transcript rows. Returns row-level results with quality scores. Max payload: 5 MB · Max rows: 30,000.

POST/v1/diff Auth required

Request Body

NameTypeDescription
original*arrayRow objects from the baseline / original version.
reworked*arrayRow objects to compare against.
configobjectOptional algorithm overrides. See .
headersstring[]Column names — required when using 2-D array input.
columnMappingobjectColumn index map for 2-D array input. See .

Row object fields

All fields are optional except transcript. Unknown fields are passed through unchanged.

NameTypeDescription
transcript*stringThe text content of the row.
speakerstringSpeaker name or ID.
start_timenumber|stringSegment start time in seconds.
end_timenumber|stringSegment end time in seconds.
non_speech_eventsstringAnnotations such as [music], [laughter].
emotionstringEmotion label.
languagestringLanguage code (e.g. "en", "ar").
localestringLocale code (e.g. "en-US").
accentstringAccent tag.

Response Shape

All successful responses use this envelope:

json
{
  "status": "success",
  "requestId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2026-04-08T21:00:00.000Z",
  "data": {
    "results": [
      {
        "status": "MODIFIED",
        "originalRow": { "transcript": "Hello world", ... },
        "reworkedRow": { "transcript": "Hello there", ... },
        "notes": "transcript changed"
      },
      {
        "status": "SPLIT",
        "originalRow": { "transcript": "Good morning everyone", ... },
        "reworkedRows": [ { "transcript": "Good morning" }, { "transcript": "everyone" } ],
        "notes": "split into 2 rows"
      }
    ],
    "scores": { "CER": 0.12, "WER": 0.18, "SER": 0.33, "cerT": 0.12, "werT": 0.18 },
    "composite": { "score": 3.8, "grade": "B", "label": "Good" },
    "meta": { "originalRows": 2, "reworkedRows": 3, "headers": [...] }
  }
}

Diff statuses

StatusMeaning
UNCHANGEDRow is identical in both versions.
MODIFIEDRow exists in both versions but content changed.
ADDEDRow is only present in the reworked version.
DELETEDRow is only present in the original version.
SPLITOne original row was divided into two or more reworked rows.
MERGEDTwo or more original rows were combined into one reworked row.

Scores

NameTypeDescription
CERnumberCharacter Error Rate across all columns (0–1, lower is better).
WERnumberWord Error Rate across all columns (0–1).
SERnumberSegmentation Error Rate — proportion of rows that were split or merged.
cerTnumberCER computed on the transcript column only.
werTnumberWER computed on the transcript column only.

Composite grade

NameTypeDescription
scorenumberWeighted quality score (1.0–5.0, higher is better).
gradestringLetter grade: A, B, C, D, or F.
labelstringHuman-readable label — e.g. "Excellent", "Good", "Needs Work".

Config Options

Pass a config object in the request body to override algorithm defaults. All fields are optional.

NameTypeDescription
simpleModebooleanDisable split and merge detection. Pure row-by-row diff. Default: false.
enableSplitsbooleanEnable split row detection. Default: true.
enableMergesbooleanEnable merge row detection. Default: true.
enableCERbooleanCompute Character Error Rate. Default: true.
enableWERbooleanCompute Word Error Rate. Default: true.
enableSERbooleanCompute Segmentation Error Rate. Default: true.
stripDiacriticsbooleanNormalise Arabic/accented characters before comparison. Default: true.
positionalModebooleanCompare rows strictly by position, skipping alignment. Default: false.
ignoreColNamesstring[]Column names excluded from MODIFIED detection. Default: [].
enableInlineDiffbooleanInclude transcriptDiff on MODIFIED rows. Set false to skip char-level diff and reduce response size. Default: true.
structuralTransformsTransformRule[]Pre-comparison find/replace rules applied to both sides before similarity scoring (max 20 rules). Default: [].

Column Mapping

When original / reworked are 2-D arrays (arrays of arrays) instead of objects, supply headers and/or columnMapping to tell the engine which index carries each field.

json
{
  "original":      [[0, 1, "Alice", "Hello world"]],
  "headers":       ["start_time", "end_time", "speaker", "transcript"],
  "columnMapping": { "transcript": 3, "speaker": 2, "start_time": 0, "end_time": 1 }
}
NameTypeDescription
transcript*integer0-based column index of the transcript field.
speakerinteger0-based column index of the speaker field.
start_timeinteger0-based column index of start time.
end_timeinteger0-based column index of end time.
nseinteger0-based column index of non-speech events.
extraColsinteger[]Additional column indices to include (max 20).

Error Reference

All errors use a uniform envelope:

json
{
  "status": "error",
  "requestId": "550e8400-...",
  "timestamp": "2026-04-08T21:00:00.000Z",
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": [{ "field": "original", "message": "\"original\" is required" }]
  }
}
HTTPCodeCause
400BAD_REQUESTMalformed JSON body
401UNAUTHORIZEDMissing or invalid x-api-key header
404NOT_FOUNDUnknown endpoint
413PAYLOAD_TOO_LARGERequest body exceeds 5 MB
422VALIDATION_ERRORBody failed schema validation (see details array)
429RATE_LIMIT_EXCEEDEDBurst or window rate limit hit
500INTERNAL_ERRORUnexpected server or engine error

Request Tracing

Provide an x-request-id header to correlate requests across your system. Alphanumeric characters, hyphens, and underscores only, max 64 characters. The value is echoed back in the response headers.

bash
curl -H "x-request-id: job-2026-01-batch-3" \
  -H "x-api-key: YOUR_KEY" \
  -X POST https://structural-diff-engine.onrender.com/v1/diff -d '{...}'

Get API Access

The API is available to agencies and teams in tasting phase. Keys are provisioned individually. Reach out to receive your key and start integrating.

Structural Diff API · v1.0 · Built by Mohamed Yaakoubi← Back to SheetDiff™