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.

EndpointPurpose
GET /accountCredit balance and payment-hold state
GET /offersPublic credit packs and unit rates
POST /quoteValidate text and obtain the exact credit reservation
POST /extractExtract uploaded text; send raw file bytes with Truessay-Filename
POST /scansSubmit a new check; requires Idempotency-Key
GET /scansList your scans; follow nextCursor for older records
GET /scans/{id}Read status and progress
GET /scans/{id}/reportRetrieve completed or partial evidence
POST /scans/{id}/cancelCancel an active scan and refund its reservation
DELETE /scans/{id}Delete submitted text, evidence and report
GET /scans/{id}/webhookInspect completion delivery state
POST /scans/{id}/webhookRetry a failed completion delivery
GET /activityRetrieve 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 / 415Invalid input, format or language; correct the request.
401 / 403Invalid key or insufficient scope; check credentials.
402Insufficient credits or payment hold; resolve billing.
404 / 410Missing, deleted or expired content; owned resources only.
409Idempotency conflict or report not ready.
429Rate, monthly spending or active-scan limit; honor Retry-After.
500 / 503Temporary 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.