← VERIFY for Journals

API reference

Three endpoints. Your platform posts a manuscript, we answer in under a second, and we call you back when the check finishes.

Keys

Every journal gets two: isi_test_… runs the full workflow and bills nothing, isi_live_… draws on your prepaid balance. Build against the sandbox for as long as you need — nothing is charged, and sandbox manuscripts are never added to the corpus.

Authorization: Bearer isi_live_9c21…
We store keys hashed. A key is shown once, when it is issued. If it is lost we revoke it and issue another — we cannot read it back to you, because we do not hold it.

Submit a manuscript

POST /api/v1/checks

curl -X POST https://verify.intelliscan.africa/api/v1/checks \
  -H "Authorization: Bearer isi_live_9c21…" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: jahs-2026-0412-r2" \
  -d '{
    "manuscript_id": "JAHS-2026-0412",      // optional — omit and we assign one
    "version": 2,
    "title": "Serum ferritin as a predictor of post-partum anaemia",
    "article_type": "research_article",
    "corresponding_author": { "name": "Dr Chinwe Okeke", "email": "c.okeke@example.ac.ng" },
    "co_authors": [ { "name": "Dr A. Adeyemi", "email": "a.adeyemi@example.ac.ng" } ],
    "callback_url": "https://journal.example.ac.ng/hooks/verify",
    "text": "Serum ferritin was measured at delivery and again…"
  }'

Answered immediately — 202 Accepted

{
  "check_id": "ISI-88214",
  "status": "queued",
  "manuscript_id": "JAHS-2026-0412",
  "manuscript_id_source": "journal",     // or "assigned" if we minted it
  "version": 2,
  "authors_recognised": 2,
  "estimated_seconds": 90,
  "billing": { "charged_ngn": 3300, "wallet_balance_ngn": 125400 }
}

Sending a Word file instead

Use document in place of text. We unpack the .docx and read its body — headers, footers and footnotes are left out on purpose, so a running header repeated on ninety pages cannot dominate the comparison.

"document": { "filename": "manuscript-r2.docx", "content_base64": "UEsDBBQABgAIAAAAIQ…" }
PDF is not accepted, on purpose. Extracting text from a PDF means parsing content streams and font encodings, and getting it slightly wrong yields plausible-looking nonsense rather than an error — which would quietly distort every similarity score computed against it afterwards. A PDF is refused with 415. Send .docx, or the extracted text; most platforms, OJS included, already hold it.

Idempotency

Send Idempotency-Key on every submission. If your server retries a request that actually succeeded, you get the original check back with "idempotent_replay": true — not a second charge, and not a duplicate in the corpus. Keys are scoped to your API key, so your references cannot collide with another journal's.

Read a check

GET /api/v1/checks/{check_id} — if you would rather poll than receive a callback.

The callback

When the check finishes we POST the result to your callback_url (HTTPS only, outside local development).

X-ISI-Event: check.completed
X-ISI-Signature: sha256=4b1f8e…

{
  "event": "check.completed",
  "check_id": "ISI-88214",
  "manuscript_id": "JAHS-2026-0412",
  "version": 2,
  "status": "completed",
  "result": {
    "similarity": 7, "verbatim": 3, "ai_advisory": 18,
    "band": "GREEN", "threshold": 15,
    "verdict": "Approved", "decision": "awaiting_editor"
  },
  "excluded": [ {
    "reason": "same_manuscript_id",
    "reason_text": "Earlier version of this manuscript",
    "check_id": "ISI-88090", "version": 1,
    "match_percent": 94,
    "authorised_by": "c.okeke@example.ac.ng"
  } ],
  "sources": [
    { "title": "Iron status in the puerperium…", "percent": 4, "class": "corpus" },
    { "title": null, "percent": 1, "class": "sealed" }
  ],
  "certificate": null
}
excluded is always present. It lists every match we set aside — earlier rounds of the same manuscript — with the real overlap and whose authority it rested on. Show it to your editors. A report that hides what it set aside is exactly as untrustworthy as one that hides what it found.

A "class": "sealed" source is another journal's unpublished manuscript. You are told a match exists and its size, and nothing else — which is precisely how your own manuscripts appear in everyone else's reports.

Verify the signature before you trust the result

Otherwise anyone who learns your callback URL can post a clean report into your editorial system.

// Node — compute over the RAW body, before any JSON parsing
const expected = 'sha256=' + require('crypto')
  .createHmac('sha256', YOUR_WEBHOOK_SECRET)
  .update(rawBody, 'utf8').digest('hex');

if (req.headers['x-isi-signature'] !== expected) return res.status(401).end();

Account and balance

GET /api/v1/account — mode, fee, threshold, wallet balance and checks_remaining, so you can warn your own staff before checks start failing.

Errors

StatusCodeWhat it means
400missing_title / missing_text / text_too_shortThe manuscript was incomplete.
401unauthorizedMissing, malformed or revoked key.
402insufficient_balanceThe wallet will not cover this check. We refuse rather than let a balance go negative; the response carries balance_ngn and required_ngn.
404not_foundNo check with that id on your account.
415unsupported_documentA PDF or other format we do not read. Send .docx or extracted text.
422document_unreadable / document_emptyThe .docx was damaged, or holds no body text (a scan or an image).

Two ways in

Neither is preferred, and nothing above changes between them — the plugin is a wrapper around exactly these endpoints.

RouteSuitsWhat your team does
OJS pluginOpen Journal Systems 3.3.xCopy the plugin folder in, enable it, paste a key. No code.
Ported pluginOJS 3.4 / 3.5, or a forkSame plugin, with the hook names and class loading updated — the README lists what moves.
Direct APIAny other platform, or a bespoke oneThe three calls above. A PHP client is included and has no OJS dependency, so it drops into any PHP platform; anything else can call the endpoints directly.
You are not committed to a route by starting. Every option authenticates with the same key, sends the same payload and receives the same callback, so a journal can begin on the direct API and move to a plugin later without anything on our side changing.

Talk to us and we will set your journal up on whichever fits.

Intelliscan Standards Institute · VERIFY for Journals · Privacy