VeriTrustLab

DEVELOPER API

Build around the evidence.

Integrate email investigation, scoped evidence retrieval, and package verification with clear request contracts and explicit failure handling.

Updated Technical report, source handlers, and read-only route checks

Interface
HTTP / JSON / NDJSON
Access
Scoped server-side keys
Evidence
Complete signed packages

Deployment and version boundaries

This guide reconciles the 12 October 2026 technical report with the production-integration implementation synchronized into main on 13 October. A report records a particular source/runtime snapshot; a route definition, healthy host, or HTTP 200 does not prove model readiness, successful persistence, or final mail delivery.

Integrated release contracts
CapabilityProduction-integration implementation12 October reference
Text and direct raw emailCanonical handlers are present; text GET returns 405 because POST is required.Evidence-aware text and original-email investigation.
Upload reservation and reference analysis/uploads reserves a private original email; /analyze-upload verifies and analyzes the uploaded reference.Private Storage, reservation expiry, and size/hash validation require the compatible deployed schema.
Full-package verificationPOST handler accepts evidence + Passport; GET correctly returns 405.Signature, both hashes, key identity, and configured issuer trust.
Key lifetime and provisioningOwner/admin creation is plan-entitled. lifetime_days accepts 1–90 days and defaults to 30; authentication checks expiry and active membership.Keys are returned once, stored as hashes, and exposed later as masked metadata.
Model hosting and schedulesPrivate AWS inference uses production workload identity. Model readiness remains separate from web/API availability.The reviewed pilot used private AWS classifiers and a 07:00–19:00 IST window.

Older releases may lack private intake and current key-lifecycle enforcement. Confirm the deployed handler/schema before reusing this guide with another installation. The documented pilot’s availability, capacity, receiver assignment, and benchmarks are separate from current route presence.

Authentication and API-key handling

Use a VeriTrust developer key as a Bearer token over HTTPS from a trusted backend or private script. The official browser uses its own same-origin session flow; do not copy browser tokens or embed developer keys in frontend JavaScript, mobile binaries, public repositories, or shared notebooks.

Authorization: Bearer vtg_live_YOUR_API_KEY

Use API Access to view and manage keys permitted by your workspace plan and role. The raw key is returned once; later list responses show masked/prefix metadata. The stored key hash is not a recoverable secret. Never log request Authorization headers or signed upload tokens.

Gateway access checks the key/session’s organization, scopes, integration, feature availability, and applicable usage limits. A provided org_id cannot select a foreign organization. Key creation requires an active owner/admin membership and an enabled workspace plan. Keys expire after the configured lifetime; revocation, inactive membership, and inactive integrations also prevent later use.

Scopes and management permissions

Default detection/Gateway scopes are filtered by enabled modules. Request only the permissions your integration needs. A valid key can still be denied by an inactive integration, plan, quota, or workspace boundary.

Supported scope names
ScopePurpose
phishing:scanStandalone /api/v1/phishing classifier.
link:scanStandalone /api/v1/link-check URL-string classification.
usage:readKey usage and workspace limits.
gateway:scanCanonical email-v2 and bounded generic Gateway submissions.
gateway:readAuthorized scan, evidence, report, and review reads.
gateway:cancelRequest cancellation of unfinished Gateway work.
gateway:policy:readPolicy inventory and operations reads.
gateway:policy:writePolicy creation/activation and review resolution.
gateway:webhook:manageWebhook configuration, test, and disable operations.

Management scopes are not included in the default six scopes. Management writes additionally require owner/admin permission. A scope name alone does not authorize a foreign tenant or a receiver deployment.

Canonical email-text investigation

POST /api/v2/phishing/analyze-text requires gateway:scan, JSON, and an Idempotency-Key. Submit a subject, body, or both; the subject limit is 998 characters and the body limit 12,000. The strict schema rejects unknown fields.

{
  "subject": "Payment request for review",
  "body": "Please confirm this invoice through our usual contact.",
  "channel": "email",
  "retention_policy": "metadata_only"
}
curl -X POST "https://YOUR_DOMAIN/api/v2/phishing/analyze-text" \
  -H "Authorization: Bearer $VERITRUST_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: YOUR_STABLE_REQUEST_ID" \
  --data-binary @message.json

Accepted optional fields are channel, locale_hint, retention_policy, org_id, integration_id, and parent_scan_id. The channel contract accepts email (default) or sms; this guide’s product workflow is email. A locale hint is bounded to 32 characters. Retention values are none, metadata_only, temporary_file, or retained_file, subject to route and deployment behavior.

Plain text has no original MIME, headers, attachment, or directly observed SMTP facts. A linked parent must be a completed same-workspace investigation at an earlier evidence stage; linkage is not independent same-message proof. Inspect returned coverage and limitations instead of treating missing checks as passed.

Direct .eml transport and limits

POST /api/v2/phishing/analyze-eml accepts original RFC822 bytes with Content-Type: message/rfc822, Gateway scan permission, and an Idempotency-Key. Preserve bytes using --data-binary rather than converting the message into pasted JSON text.

curl -X POST "https://YOUR_DOMAIN/api/v2/phishing/analyze-eml" \
  -H "Authorization: Bearer $VERITRUST_API_KEY" \
  -H "Idempotency-Key: YOUR_STABLE_REQUEST_ID" \
  -H "Content-Type: message/rfc822" \
  --data-binary @original.eml

The parser contract allows up to 10 MiB, but the deployment’s HTTP request-body limit can be lower. The report records Vercel’s 4.5 MB direct-body boundary. For larger originals within the 10 MiB intake limit, reserve a private upload and analyze its reference rather than sending the entire file through the Vercel function.

Independent parsing boundaries
BoundaryLimit
Header bytes256 KiB
MIME depth / parts10 / 100
Decoded total / attachment bytes25 MiB / 10 MiB
Normalized HTML / parser time1 MiB / 5 seconds

The direct route accepts X-Retention-Policy values ephemeral_24h or temporary_file and an optional X-VeriTrust-Parent-Scan-Id. A retention request is not proof of completed deletion. Raw EML enables message-carried DKIM/ARC/alignment and relay evidence; trusted SPF requires directly observed SMTP facts.

Private RFC822 intake

The integrated runtime implements the reservation and reference-analysis routes below. It requires compatible private Storage policies and intake RPCs. Upload bytes go directly to the authorized Storage object; analysis receives a reference and verifies the stored size and hash.

  • Compute the original file’s byte count and lowercase SHA-256 before reserving it.
  • POST /api/v2/phishing/uploads with Gateway scan permission, JSON, and a stable Idempotency-Key. Required fields are size_bytes and sha256; optional fields are input_mode, parent_scan_id, and integration_id.
  • A 201 response contains upload.id, upload_url, storage_origin, expires_at, and max_bytes. Validate the signed URL’s HTTPS origin and expected single-object path against the authorized response.
  • PUT message/rfc822 bytes to that signed Storage URL. Do not forward the VeriTrust API key, cookies, or receiver secret to Storage; do not follow arbitrary redirects.
  • POST only {"upload_id":"…"} to /api/v2/phishing/analyze-upload using the same identity and Idempotency-Key. The server verifies original size/hash and cannot upgrade the stored input mode through this request.
{
  "size_bytes": 1024,
  "sha256": "REPLACE_WITH_64_LOWERCASE_HEX_DIGITS",
  "input_mode": "raw_eml"
}

The reservation expires after 15 minutes; Storage-token validity and cleanup are separate windows. Its 10 MiB original-email limit does not grant permission for arbitrary media. Trusted-receiver reservations require the separate receiver authorization and validated transport context.

Retries, progress, and final responses

Email-v2 and generic Gateway submissions require an Idempotency-Key containing 1–200 printable characters. Generate and persist one identifier per logical input; reuse it for the same input and identity. A changed request with an already-used key can return 409. Standalone v1 classifiers do not implement this Gateway replay contract merely because a caller supplies the header.

Optional X-Request-ID and X-Trace-ID support correlation. Do not confuse a request ID with a stored scan ID, a delivery receipt, or a replay guarantee. Inspect scan status and any returned degraded/failed evidence before recording success.

For supported analysis paths, Accept: application/x-ndjson requests progress on the same authenticated HTTP request. This is newline-delimited JSON, not WebSockets or a durable in-memory job service. Ignore heartbeat blank lines, process sequenced progress events, and use the final result event’s status and payload.

{
  "type": "result",
  "status": 503,
  "payload": {
    "ok": false,
    "error": {
      "code": "EXAMPLE_FAILURE",
      "message": "Illustrative failed final event"
    }
  }
}

An outer HTTP 200 may already have been sent when streaming begins. It does not make a failed final event successful. A 202 response is pending work rather than a completed signed investigation. Retry transient failures only when the route’s idempotency contract supports safe replay; fix schema, identity, expiry, or permission errors first.

Evidence retrieval, exports, and verification

Use GET /api/v2/phishing/evidence/{scan_id} with Gateway read permission for authorized evidence. Export routes use /api/v2/phishing/export/{scan_id}/{format} with json, csv, iocs, or stix. The default export path without a format is JSON. The iocs format describes observed artifacts; STIX output is not independently confirmed maliciousness.

Keep the complete exported evidence JSON when cryptographic verification matters. A display summary, CSV, PDF, redacted excerpt, or Passport-only object is not the complete original signed package. If a historical record lacks the exact signed snapshot, do not reconstruct current rows and attach an old signature.

POST /api/v2/phishing/verify-evidence is a bounded public API, even though the website’s verifier page requires sign-in. It accepts only passport and evidence objects, with a 1 MiB request limit. It does not require an investigation key or an Idempotency-Key.

curl -X POST "https://YOUR_DOMAIN/api/v2/phishing/verify-evidence" \
  -H "Content-Type: application/json" \
  --data-binary @verification-request.json

Verification returns 200 for a valid package and 422 for failed checks. Inspect verification.valid, signature_valid, evidence_hash_valid, manifest_hash_valid, key_id_valid, issuer_trusted, and error codes. Key trust is defined by the target deployment’s issuer policy.

Integrity and issuer checks do not establish safe mail, sender identity, claim truth, attribution, or legal admissibility. Verifying JSON does not separately retrieve an original .eml and recompute its byte hash.

Generic Gateway and management routes

/api/v1/gateway supports bounded text/URL correlation separately from canonical email-v2 intake. POST /scans needs Gateway scan permission and an Idempotency-Key. Content accepts text (up to 12,000 characters) and up to 20 explicitly submitted HTTP/HTTPS URLs. Generic media/deepfake fields are not accepted by the production schema.

{
  "source": {
    "kind": "api"
  },
  "content": {
    "text": "Review this account message.",
    "urls": [
      "https://example.com/account"
    ]
  },
  "processing_mode": "synchronous",
  "metadata": {
    "context_categories": [
      "email"
    ]
  }
}
Gateway resource routes
Route and methodScope / behavior
GET /scans and /scans/{id}gateway:read; tenant-scoped list/status/evidence.
GET /reports/{id}gateway:read; returned report envelope.
POST /scans/{id}/cancelgateway:cancel; unfinished-work cancellation request.
GET/POST /policies; POST /policies/{id}/activatePolicy read/write scopes; write actions require manager authorization.
GET/POST /webhooks; POST /webhooks/{id}/test or /disablegateway:webhook:manage; approved destinations and manager controls.
GET /reviews; POST /reviews/{id}/resolvegateway:read for listing; gateway:policy:write for resolution.
GET /operationsgateway:policy:read; operational evidence.

Processing modes are synchronous, asynchronous, or hybrid. A callback references an authorized webhook_endpoint_id; configuring delivery is a separate management action. Signed webhooks and private signed worker invocations have different credentials and replay controls. Workers are not ordinary API-key or browser-session endpoints.

Usage and error handling

GET /api/v1/usage requires usage:read and returns masked key information, daily usage, and an available workspace usage/limits snapshot. Usage reads are themselves API usage events. Key and workspace quotas, subscription status, enabled modules, and runtime/provider availability are independent checks; do not assume a fixed quota across plans.

HTTP outcomes to handle
StatusInterpretation
400 / 415Invalid schema, input, or content type.
401 / 403Invalid authentication or denied scope/role/feature.
404Unknown/unavailable route or resource outside authorized scope; not evidence of benign content.
409 / 410Conflicting/replayed input state or expired upload in supporting runtimes.
413 / 422Payload boundary / failed evidence verification.
429 / 5xxQuota/overload / provider, persistence, or runtime failure.

API v1 responses include request_id and structured error code/message fields. Other routes have their own response envelope; do not assume every result shares the standalone v1 shape. Store bounded request/scan identifiers for support without logging credentials or raw submitted mail.

Trusted receiver and runtime prerequisites

Internal receiver-eml and receiver-event routes require both scoped Gateway authentication and the configured receiver secret. Original raw email or an authorized stored reference must be accompanied by validated client_ip, mail_from, helo, receiver_id, authserv_id, received_at, and event_id facts.

Arbitrary message headers cannot create trusted SMTP provenance. A controlled hardened edge supplies transport observations; the bare Node companion requires a private environment or a separate hardened TLS edge. Consult the receiver guide for controlled setup.

The supplied report’s connected receiver/status and Gmail pilot are operator-assigned integrations, not an automated multi-organization provisioning wizard. New organizations require explicit credential, recipient, domain/routing, delivery, and recovery validation. Analysis completion, policy disposition, and confirmed recipient delivery are separate events.

The integrated source uses private AWS classifier hosting with production workload identity. The documented pilot schedule is 07:00–19:00 IST, with startup readiness checked separately. Web deployment checks do not run model inference or prove final recipient delivery; verify those runtime outcomes before treating hosting or schedule labels as live guarantees.

Node.js example with explicit result handling

This example uses built-in Node.js APIs, performs one text investigation, preserves its response, and submits the complete evidence/Passport pair for verification. Use a fresh identifier for new input and the persisted identifier for retries. It intentionally stops on pending, failed, missing-package, or oversized-verification outcomes.

// Save as investigate.mjs and run with Node.js 24.
// Provide VERITRUST_API_KEY through your private server environment.
import { randomUUID } from 'node:crypto';
import { writeFile } from 'node:fs/promises';

const base = 'https://www.veritrustlab.in';
const apiKey = process.env.VERITRUST_API_KEY;
if (!apiKey) throw new Error('VERITRUST_API_KEY is required.');
// Persist this ID before sending; reuse it for a retry of this input.
const requestId = process.env.VERITRUST_REQUEST_ID || randomUUID();
console.log('Retry identity:', requestId);

const response = await fetch(base + '/api/v2/phishing/analyze-text', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + apiKey,
    'Content-Type': 'application/json',
    'Idempotency-Key': requestId,
    Accept: 'application/json'
  },
  body: JSON.stringify({
    subject: 'Payment request for review',
    body: 'Please confirm this invoice through our usual contact.',
    channel: 'email',
    retention_policy: 'metadata_only'
  }),
  signal: AbortSignal.timeout(190000)
});
const result = await response.json();
if (response.status === 202) {
  throw new Error('Not finalized. Retain the retry identity and review scan status.');
}
if (!response.ok || result.ok !== true) {
  throw new Error(result.error?.message || 'Investigation request failed.');
}

// Preserve the returned package before making any display/redaction changes.
await writeFile('investigation.json', JSON.stringify(result, null, 2));
const evidence = result.evidence;
const passport = evidence?.evidence_passport;
if (!evidence || !passport) {
  throw new Error('No complete signed package is available; do not reconstruct one.');
}
const body = JSON.stringify({ passport, evidence });
if (Buffer.byteLength(body, 'utf8') > 1024 * 1024) {
  throw new Error('The complete verification request exceeds 1 MiB.');
}
// The public verification route does not require the investigation API key.
const checked = await fetch(base + '/api/v2/phishing/verify-evidence', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
  body,
  signal: AbortSignal.timeout(30000)
});
const checkedBody = await checked.json();
if (![200, 422].includes(checked.status) || !checkedBody.verification) {
  throw new Error('Verification did not return a usable result.');
}
console.log(checkedBody.verification);
if (!checkedBody.verification.valid) {
  throw new Error('Signed package verification failed.');
}

There is no official VeriTrustLab SDK in this repository. Do not share a notebook or sample containing a live key. Code examples demonstrate contracts; they do not certify inference availability, a production SLA, or valid results for arbitrary input.

Security, retention, and further references

  • Keep keys, receiver credentials, and webhook/worker signing material server-side. Use narrow scopes and rotate/revoke access when required.
  • Preserve complete signed records before formatting or redaction. Exported entities and hashes can still contain sensitive identifiers.
  • Retention and erasure are hold-aware governance workflows; a deadline or an erasure request is not proof that every copy was deleted.
  • Treat classifiers, authentication, infrastructure location, and campaign overlap as bounded evidence for human review.

Read Documentation, Security, Privacy, and Limitations. The email-v2 and Gateway OpenAPI files describe versioned schemas; reconcile them with deployed handlers because newer specifications can precede runtime availability.