API reference · Private beta
Build with context.
Practical client guide for image, sampled video, text, audio, jobs and review dossiers.
Authentication and environment
All endpoints except health checks use a tenant API key. Evaluation keys are issued manually during private beta.
export BASE_URL=http://127.0.0.1:8799/v1
export API_KEY='[REPLACE_WITH_TENANT_KEY]'Send Authorization: Bearer $API_KEY. Read operations accept a valid tenant key; writes require the detect scope. Keys are tenant-scoped, revocable and rate-limited. Never expose one in browser code, URLs or logs.
GET /v1/model-infoReturn active image model, backend, thresholds and warning-only policy. Runtime metadata is not an accuracy certificate.Request units and response anatomy
| Lane | Units | Status |
|---|---|---|
| Image | 1 per request | Core |
| Text | 1 per request | Experimental |
| Audio | 4 per request | Experimental |
| Video | 1 per submitted frame | Core aggregate |
Common result semantics
| Field | Meaning |
|---|---|
status | known, unknown or processing; jobs add completed/failed. |
label | Lane-specific warning label. It is not an origin verdict. |
score / confidence | Raw model outputs, not calibrated probabilities unless a benchmark says so. |
source / cached | Whether the response came from model, cache, store or aggregate. |
model / reason | Active version and quality/unknown explanation where supported. |
Image/video labels are synthetic_indicators, possible_synthetic_indicators, no_strong_ai_signal and unknown. Text/audio currently use provisional likely_ai, possible_ai, likely_human and unknown. These labels are not interchangeable between lanes.
Image check
POST /v1/assets/check accepts JSON with base64 JPEG or multipart with a JPEG/PNG file.
curl "$BASE_URL/assets/check" \
-H "Authorization: Bearer $API_KEY" \
-F "[email protected]" \
-F "client_asset_id=asset-001" \
-F "sync=true"{
"client_asset_id": "asset-001",
"thumb_jpeg_base64": "<base64 omitted>",
"sync": true
}Request fields
| Field | Type | Required / meaning |
|---|---|---|
file | JPEG/PNG | Multipart image unless fingerprints resolve |
thumb_jpeg_base64 | string | JSON low-resolution JPEG unless fingerprints resolve |
client_asset_id | string | Optional caller reference/idempotency identifier |
frame_sha256 / frame_phash | string | Optional exact / perceptual fingerprints |
sync | boolean | Optional; true by default, false queues fresh inference |
Representative response
{
"asset_id": "asset_example", "job_id": null, "status": "known",
"label": "possible_synthetic_indicators", "score": 0.84,
"confidence": null, "thresholds": {"possible_synthetic_indicators": 0.8},
"source": "model", "model": "active-model-version",
"cached": false, "ttl_seconds": 86400
}Identifiers and numbers illustrate the schema; they are not benchmark results or guaranteed outputs.
Sampled video
POST /v1/videos/check-frames accepts up to 8 image request objects plus a stable video_footprint. Use 5–8 representative frames. This is not temporal deepfake analysis.
{
"video_footprint": "stable-footprint",
"frames": [{"thumb_jpeg_base64": "[BASE64_OMITTED]"}],
"sync": true
}| Field | Type | Constraint |
|---|---|---|
video_footprint | string | Stable caller fingerprint; recommended |
frames | AssetCheckRequest[] | 0–8 frames; use 5–8 on the first request |
sync | boolean | Default true |
Representative aggregate response
{
"status": "known", "label": "possible_synthetic_indicators",
"score": 0.83, "median_score": 0.83, "mean_score": 0.82,
"max_score": 0.86, "frame_count": 6, "scored_frame_count": 6,
"pending_frame_count": 0, "frames": [], "cached": false,
"source": "video_aggregate"
}A real response includes per-frame response objects. Repeat with the same footprint and no frames for a cache lookup. A miss returns unknown.
Experimental text
POST /v1/text/check is a warning-only writing-style signal. Samples below 60 words normally return unknown / too_short.
| Field | Type | Constraint |
|---|---|---|
text | string | Required; 1–20,000 characters |
language | string | Optional language hint |
canonical_url | string | Optional source reference |
platform_asset_id | string | Optional caller reference |
{"text":"A representative long-form sample...", "language":"en"}Representative response
{
"status": "known", "label": "unknown", "score": 0.41,
"confidence": 0.18, "source": "text_hybrid",
"model": "text-hybrid-v2-warning-only", "cached": false,
"text_hash": "illustrative-sha256", "char_count": 310,
"word_count": 61, "reason": "model-specific diagnostic",
"policy": "weak_signal_warning_only"
}Experimental audio
POST /v1/audio/check accepts PCM WAV. Quiet, short or tonal non-speech clips can return unknown before scoring.
| Field | Type | Constraint |
|---|---|---|
file | PCM WAV | Multipart upload |
audio_base64 | string | JSON alternative to file |
filename | string | Optional source filename |
canonical_url / platform_asset_id | string | Optional source references |
curl "$BASE_URL/audio/check" -H "Authorization: Bearer $API_KEY" -F "[email protected]"Representative response
{
"status": "known", "label": "unknown", "score": null,
"confidence": null, "source": "audio_detector",
"model": "audio-wav2vec2-v1-warning-only", "cached": false,
"audio_hash": "illustrative-sha256", "duration_seconds": 4.2,
"sample_rate": 16000, "reason": "audio_backend_disabled",
"policy": "weak_signal_warning_only"
}Jobs and stored assets
Set sync=false for asynchronous image inference. The initial response has status: processing and a job_id.
GET /v1/jobs/{job_id}Poll processing, completed or failed. Completed jobs include result.GET /v1/assets/{asset_id}Retrieve the tenant-scoped complete result.{"job_id":"job_example","status":"completed","result":{"asset_id":"asset_example","status":"known","label":"possible_synthetic_indicators"}}Recommended client policy: poll after 1, 2, 4, 8 and then 10 seconds; cap each later delay at 10 seconds and stop after 60 seconds or on completed/failed. The server does not currently guarantee that timeout and exposes no webhook or Retry-After contract.
Human review lifecycle
POST /v1/reviewsMultipart file, required declared_ai_use, optional supplier and notes.GET /v1/reviewsList tenant dossiers; no pagination contract during beta.GET /v1/reviews/{review_id}Retrieve one dossier.POST /v1/reviews/{review_id}/decisionRecord disclose, no_disclosure, reject or recheck.GET /v1/reviews/{review_id}/exportExport JSON.DELETE /v1/reviews/{review_id}Delete; success is 204.curl "$BASE_URL/reviews" -H "Authorization: Bearer $API_KEY" -F "[email protected]" -F "declared_ai_use=unknown" -F "supplier=Example supplier"{"decision":"disclose","reviewer":"[email protected]","rationale":"Disclosure selected after source and detector review."}Review responses include review_id, pending/completed status, content hash, filename, declaration, provenance, detector, optional decision, policy and created/updated timestamps. They never automatically reject or publish content.
HTTP status
| Status | Meaning |
|---|---|
| 200 / 204 | Completed or deleted |
| 400 | Invalid or unsupported input |
| 401 / 403 | Missing key, invalid key or insufficient scope |
| 404 | Tenant-scoped resource not found |
| 413 | Upload exceeds configured maximum |
| 422 | Typed JSON validation failed |
| 429 | Per-key rate limit exceeded |
| 503 | Production dependency or worker not ready |
Errors use {"detail":"error_code"}; for example {"detail":"invalid_api_key"}. Every response includes X-Request-ID. The current async image submission still returns the normal endpoint response with status: processing; do not assume HTTP 202.