Turing Verify REST API — v1¶
Base URL: https://api.verify.turingcerts.com
Protocol: HTTPS + JSON. All timestamps are RFC-3339 UTC (YYYY-MM-DDTHH:MM:SSZ).
This page is the canonical reference for the /v1 public API. It is served
at /v1/docs.md (raw markdown) and rendered as HTML at /v1/docs/reference.
For the OpenAPI 3.1 schema use /v1/openapi.json. For the Model Context
Protocol connection, see /mcp/docs.
- Authentication
- Environments: test vs live
- Error envelope
- Rate limits
- Idempotency
- Pagination
- Endpoints
- Webhooks
- MCP
- SDKs
Authentication¶
Every request must carry a bearer token:
Authorization: Bearer tv_live_sk_<secret>
Tokens are minted from the Developer Console (/developers/tokens). Prefixes:
tv_live_sk_...— production, charges credits.tv_test_sk_...— deterministic, zero-credit.
Environments¶
- Test mode — tokens starting with
tv_test_sk_. Deterministic verdicts seeded fromsha256(bytes) % 4, no model calls, zero credits. Use in CI and for offline integration tests. - Live mode — tokens starting with
tv_live_sk_. Runs the full forensic pipeline, charges 1 credit per verification (2 for deep mode). Response headers carryX-Credits-Remainingand, when quota crosses 80 %,X-Credits-Warning: approaching-quota.
The server echoes the mode in X-Turing-Environment: test|live on every
response.
Error envelope¶
Non-2xx responses always have this shape:
{
"error": {
"code": "invalid_request",
"message": "Human-friendly description.",
"request_id": "req_01HTN8Z9MJB00000000000000",
"docs_url": "https://api.verify.turingcerts.com/v1/docs#errors"
}
}
Common code values:
code |
HTTP | Meaning |
|---|---|---|
authentication_required |
401 | Missing or malformed Authorization header. |
invalid_api_key |
401 | Token unknown, revoked, or expired. |
insufficient_scope |
403 | Token is valid but lacks the required scope. |
plan_required |
403 | Endpoint is gated to Pro / Business. |
not_found |
404 | The resource does not exist (or is not yours). |
idempotency_conflict |
409 | Idempotency-Key reused with a different body. |
invalid_request |
400 | Validation failed (see message). |
rate_limited |
429 | Token bucket exhausted or quota depleted. |
internal_error |
500 | Server problem; retry with exponential backoff. |
X-Request-ID is also sent as a response header — include it when filing
support tickets.
Rate limits¶
Per-token, token-bucket scheme. Headers on every response:
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 597
X-RateLimit-Reset: 42
Limit— sustained requests/minute for your tier.Remaining— requests left in the current window.Reset— seconds until the window resets.
Tiers (defaults; contact sales for custom):
| Plan | Burst | Sustained (req/min) |
|---|---|---|
| Pro | 20 | 600 |
| Business | 40 | 3000 |
When you breach the limit you get 429 rate_limited with a Retry-After
header (seconds).
Idempotency¶
POST /v1/verifications supports an Idempotency-Key header. Replays
within 24 hours return the cached response with
Idempotency-Status: replayed. Reusing the same key with a different body
returns 409 idempotency_conflict.
Every official SDK generates a UUID4 key automatically — you only need to set this header by hand if you're calling the API directly.
Pagination¶
All list endpoints use cursor pagination. Query params:
limit— page size, 1–100 (default 20).starting_after— opaque cursor from the previous page'snext_cursor.
Response shape:
{
"object": "list",
"data": [ ... ],
"has_more": true,
"next_cursor": "ver_01HTN..."
}
Endpoints¶
POST /v1/verifications¶
Submit a document for verification. Accepts a multipart upload (file) or a
JSON body with document_url or document_base64 (+ content_type).
Request (multipart)
curl -X POST https://api.verify.turingcerts.com/v1/verifications \
-H "Authorization: Bearer tv_live_sk_..." \
-F "file=@diploma.pdf"
Response 200
{
"id": "ver_01HTN...",
"status": "completed",
"verdict": "ACCEPT",
"confidence": 0.94,
"doc_type": "diploma",
"livemode": true,
"created_at": "2026-04-17T09:12:33Z",
"poll_url": "/v1/verifications/ver_01HTN..."
}
GET /v1/verifications/{id}¶
Fetch a previously submitted verification.
curl https://api.verify.turingcerts.com/v1/verifications/ver_01HTN... \
-H "Authorization: Bearer tv_live_sk_..."
404 if the id is unknown or the verification belongs to another user — we don't leak existence.
GET /v1/verifications¶
Cursor-paginated list of your verifications, newest first.
Query params: limit, starting_after, verdict (optional filter).
POST /v1/verifications/batch¶
Business-tier only. Submit up to 1 000 documents in a single request.
{
"documents": [
{ "base64": "JVBERi...", "content_type": "application/pdf" },
{ "url": "https://example.com/cert.pdf" }
]
}
POST /v1/webhooks¶
Register a webhook endpoint. Returns the signing secret once.
{
"url": "https://example.com/hooks/turing-verify",
"events": ["verification.completed", "verification.failed"],
"description": "Prod ATS ingestion"
}
GET /v1/webhooks¶
List endpoints you own.
GET /v1/account¶
{
"user_id": "usr_01HTN...",
"email": "alice@example.com",
"plan": { "tier": "pro", "api_access": true, "rate_limit_burst": 20, "rate_limit_sustained": 600 },
"credits": { "included": 150, "used": 12, "remaining": 138, "reset_at": "2026-05-01T00:00:00Z", "overage_opt_in": false }
}
GET /v1/account/usage¶
Month-to-date usage histogram.
Query params:
period—YYYY-MM, defaults to the current calendar month.group_by—day(default),endpoint, ortoken.
Webhooks¶
Events are delivered as HTTPS POSTs with this envelope:
{
"id": "evt_01HTN...",
"type": "verification.completed",
"api_version": "2026-04-01",
"created": "2026-04-17T09:12:33Z",
"data": { "verification": { ... } }
}
HMAC signing¶
Every delivery includes:
X-TuringVerify-Signature: t=1713347553,v1=<hex digest>
The digest is computed as:
HMAC_SHA256(secret, f"{timestamp}.{raw_body}")
Python¶
from turingverify import verify_signature, SignatureVerificationError
body = await request.body()
try:
verify_signature(body, request.headers["X-TuringVerify-Signature"], SECRET)
except SignatureVerificationError:
return 400
TypeScript¶
import { verifyWebhookSignature } from '@turingverify/sdk';
verifyWebhookSignature(
req.rawBody,
req.header('X-TuringVerify-Signature') ?? '',
process.env.WEBHOOK_SECRET!,
);
Go (hand-rolled)¶
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(fmt.Sprintf("%d.", ts)))
mac.Write(body)
expected := hex.EncodeToString(mac.Sum(nil))
Retry ladder¶
Failed deliveries retry at
0s, 30s, 2m, 10m, 30m, 2h, 6h, 12h, 24h (nine attempts over ~45 h). After
the last failure the delivery row is marked permanently_failed; the
developer portal lets you replay one manually.
Event list (launch set)¶
verification.completedverification.failedverification.manual_review_requiredbatch.completedtoken.rotatedwebhook.ping(synthetic test fires)
MCP¶
The Model Context Protocol surface lives at
https://mcps.verify.turingcerts.com/mcp. See /mcp/docs for the full
manual (5 tools, 3 resources, 2 prompts). One-click client manifests are
served at /.well-known/mcp-config.
SDKs¶
- Python —
pip install turingverify— source in./sdks/python. - TypeScript / Node —
npm install @turingverify/sdk— source in./sdks/node.
Both SDKs wrap the endpoints documented here, handle retries, inject an
auto-generated Idempotency-Key, and ship a verify_signature /
verifyWebhookSignature helper for inbound webhooks.