Asynchronous API · v1
Evidence your software
can work with.
Submit once, follow progress, then retrieve structured passage evidence. Quick and Deep share the same lifecycle and credit balance.
Submit, poll, inspect.
All URLs begin with https://truessay.com/v1/plagiarism. Send JSON with an explicit mode and a stable Idempotency-Key. A new submission returns HTTP 202 with a scan ID; an identical replay returns the original scan.
curl https://truessay.com/v1/plagiarism/scans \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: YOUR_UNIQUE_REQUEST_ID' \
-d '{
"title": "Coastal research",
"text": "Your English document of at least 20 words…",
"mode": "deep",
"options": {"excludeQuotes": true, "excludeReferences": true},
"webhookUrl": "https://your-app.example/webhooks/truessay"
}'The text above is a placeholder. Replace it with a real document containing 20–20,000 English words. Omit webhookUrl if you prefer polling.
GET /scans/{id}
→ {"scan": {"id": "…", "status": "searching", "progress": 24, …}}
GET /scans/{id}/report
→ {"scan": {…}, "report": {"mode": "deep", "matches": […], …}}Poll every 3–5 seconds while the scan is queued, searching, retrieving or matching. Complete and partial scans expose a report. Failed and canceled scans are terminal and return their reserved credits.
Keep credentials on your server.
Use Authorization: Bearer tp_live_…. Full API keys appear once in settings; only a hash is stored. Read-only keys can view owned scans and reports. Scan keys can submit, extract, cancel, delete and retry webhooks. API keys cannot purchase credits, claim trials, create keys or reveal signing secrets.
Set a monthly credit limit on each key. The limit counts scan charges from submissions in the current UTC month, including active reservations. It does not add credits to the account. Revoke a key in settings to reject further requests immediately.
A small, complete lifecycle.
| Endpoint | Purpose |
|---|---|
GET /account | Credit balance and payment-hold state |
GET /offers | Public credit packs and unit rates |
POST /quote | Validate text and obtain the exact credit reservation |
POST /extract | Extract uploaded text; send raw file bytes with Truessay-Filename |
POST /scans | Submit a new check; requires Idempotency-Key |
GET /scans | List your scans; follow nextCursor for older records |
GET /scans/{id} | Read status and progress |
GET /scans/{id}/report | Retrieve completed or partial evidence |
POST /scans/{id}/cancel | Cancel an active scan and refund its reservation |
DELETE /scans/{id} | Delete submitted text, evidence and report |
GET /scans/{id}/webhook | Inspect completion delivery state |
POST /scans/{id}/webhook | Retry a failed completion delivery |
GET /activity | Retrieve the latest 50 credit ledger events |
For file extraction, send UTF-8 .txt / .md, Word .docx, or a text-based .pdf up to 5 MB. Set Content-Type: application/octet-stream and Truessay-Filename: paper.pdf. Extraction returns text for review; it does not create a scan or reserve credits. Image-only PDFs require OCR outside this service.
Words first. Conclusions later.
overlapPercent is the union of matched eligible input words divided by all eligible input words. Mirrored or overlapping sources do not increase the same word’s contribution. Text normalization preserves offsets into the original string.
Each match supplies inputRanges, sourceRanges, excerpts, word count, kind and scope. Offsets are zero-based UTF-16 indices with an exclusive end, compatible with JavaScript’s slice(start, end). Exact matches require at least 8 words; near-exact full-text alignment requires at least 16 words and 80% matched coverage. Edited words do not count as matches.
scope: snippet means returned search-summary text; it is not verbatim full-document context. scope: full_text means text extracted from a retrieved webpage or PDF. Use each match’s scope when displaying or exporting evidence.
Coverage records queried input ranges, completed queries, source budgets and retrieval outcomes. Retrieval may be blocked, failed, unsupported or outside budget. Quick never retrieves candidate sources. Deep may preserve snippet evidence when retrieval fails. A partial report exposes unfinished work; a failed scan is not a zero-overlap result.
Semantic analysis is disabled. No private submission corpus, proprietary journal database or originality guarantee is provided. A matching phrase does not establish copying direction, intent or misconduct.
Verify the exact payload.
Provide a public HTTPS webhookUrl on submission. A terminal scan emits plagiarism.scan.finished with an event ID and scan status. Retrieve the report separately. Requests carry Truessay-Signature: t=TIMESTAMP,v1=HEX_HMAC and Truessay-Event-Id.
import { createHmac, timingSafeEqual } from 'node:crypto';
// Read the raw body before any JSON parsing.
function verify(rawBody, header, secret) {
const fields = Object.fromEntries(header.split(',').map(x => x.split('=')));
if (!/^\d+$/.test(fields.t ?? '') || !/^[a-f0-9]{64}$/.test(fields.v1 ?? ''))
return false;
if (Math.abs(Date.now() / 1000 - Number(fields.t)) > 300) return false;
const expected = createHmac('sha256', secret)
.update(fields.t + '.' + rawBody).digest();
return timingSafeEqual(expected, Buffer.from(fields.v1, 'hex'));
}
// Reject invalid signatures; deduplicate valid events by their body.id.
// Return a 2xx response after durably accepting the event.The signing secret is available in your signed-in settings. Timestamps use Unix seconds. Delivery is at least once; deduplicate by event ID. Redirects are not accepted. Delivery retries up to 6 times, with bounded backoff. Inspect or retry failed delivery through the scan endpoint.
Predictable reservations.
Quick costs 1 credit and Deep 10 credits for every started 1,000 submitted words. Exclusions affect scoring, not billing. Credits are reserved atomically before work is queued. Each account supports 5 active scans and 10 active keys. The API permits 120 requests per minute per key, 10 scan submissions per minute per account and 10 uploads per minute per account.
Failed and canceled scans are fully refunded. A Deep check with discovered candidates but no retrieved source text is partial and billed at 2 credits per started 1,000 words. A completed Deep discovery with no candidates retains its Deep charge. Other partial reports retain the quoted charge while clearly exposing incomplete work.
Input, evidence and reports are private and removed after 30 days. Delete earlier through the API. Billing records remain for reconciliation. Search discovery sends selected input phrases to Serper; semantic processing through TypeSafe is disabled.
Retry without a second charge.
Errors return {"error":{"code":"…","message":"…"}}. Use a new request key only for a new logical submission. A key reused with different content returns 409. Deleted request keys return 410 and cannot resurrect content.
400 / 415 | Invalid input, format or language; correct the request. |
401 / 403 | Invalid key or insufficient scope; check credentials. |
402 | Insufficient credits or payment hold; resolve billing. |
404 / 410 | Missing, deleted or expired content; owned resources only. |
409 | Idempotency conflict or report not ready. |
429 | Rate, monthly spending or active-scan limit; honor Retry-After. |
500 / 503 | Temporary failure; retry the same submission key with backoff. |
For support, include the scan ID and error code. Never include API keys, signing secrets or confidential document content.