Response Envelope
Every response from the inspect endpoint follows a consistent envelope structure. The envelope separates lifecycle truth, data payload, enrichment provenance, and projection metadata into distinct semantic layers.
Envelope Structure
{
// ── Layer 1: Lifecycle Truth ──
"success": true, // boolean — request succeeded
"responseSource": "fresh", // "fresh" | "cache" | "inflight"
"cached": false, // boolean — response served from cache
// ── Layer 2: Processing State (canonical) ──
"processing": {
"status": "complete", // "partial" | "complete"
"completion": 1, // 0.0 → 1.0
"enrichmentsPending": false, // boolean
"enrichmentStatus": "complete", // mirrors status
"completedStages": ["semantic", "mediaFormat", "oembed"],
"pendingStages": []
},
// ── Layer 3: Data Payload (profile-projected) ──
"data": {
"identity": { /* ... */ },
"semantic": { /* ... */ },
"provider": { /* ... */ }, // only for provider-detected URLs
"playback": { /* ... */ },
"metadata": { /* ... */ },
"preview": { /* ... */ },
"diagnostics": { /* ... */ } // only in FULL profile
},
// ── Layer 4: Enrichment Provenance (immutable) ──
"enrichmentSources": {
"semantic": "fresh",
"mediaFormat": "fresh",
"oembed": "fresh"
},
// ── Layer 5: Projection Metadata ──
"projection": {
"profile": "full",
"hiddenFields": []
},
// ── Layer 6: Async Job (when applicable) ──
"job": { // only for 202 responses
"id": "job_abc123",
"status": "processing"
}
}Semantic Layers
Layer 1 — Lifecycle Truth
| Field | Type | Description |
|---|---|---|
success | boolean | Whether the inspection succeeded |
responseSource | string | How this response was fulfilled: "fresh", "cache", or "inflight" |
cached | boolean | Binary cache indicator — true only for cache hits |
Response source semantics:
| Value | HTTP Status | cached | Meaning |
|---|---|---|---|
fresh | 200 | false | First-time processing, enrichments ran during this request |
cache | 200 | true | Served from canonical cache |
inflight | 202 | false | Attached to an active enrichment lifecycle in progress |
Layer 2 — Processing State
Derived exclusively from the canonical enrichment registry. Never influenced by profile, field projection, or formatter visibility.
See Progressive Enrichment for detailed processing state semantics.
Layer 3 — Data Payload
The primary inspection result. Structure varies by media category and profile. Each namespace carries semantic meaning:
| Namespace | Always Present | Description |
|---|---|---|
identity | ✅ | URL, final URL, canonical URL, MIME type |
semantic | ✅ | Kind classification, semantic type, title |
playback | ✅ | Playback strategy, component, embed/stream capabilities |
metadata | ✅ | File metadata + category-specific sub-object |
provider | Provider only | Provider name, confidence, detection method |
preview | Varies | Thumbnails, placeholders, palette, waveform |
diagnostics | FULL only | Performance timings, internal metrics |
network | Debug only | Raw headers (requires ?expand=rawHeaders&debug=true) |
Layer 4 — Enrichment Provenance
Immutable record of where each enrichment was originally produced. Never mutated by cache hits — responseSource may change to "cache", but enrichmentSources preserves the original provenance.
See Provider Intelligence for provenance semantics.
Layer 5 — Projection Metadata
Declares the active profile and any fields that were intentionally hidden by the projection layer.
// FAST direct media — preview is projection-hidden
{
"projection": {
"profile": "fast",
"hiddenFields": ["preview"]
}
}
// FAST provider media — provider-native preview is visible
{
"projection": {
"profile": "fast",
"hiddenFields": []
}
}HTTP Status Codes
| Status | Condition | Profile |
|---|---|---|
200 | Inspection complete or served from cache | FAST, FULL, cached STANDARD |
202 | Enrichments still processing, job dispatched | STANDARD (heavy media) |
400 | Invalid URL or malformed payload | Any |
403 | SSRF protection blocked the request | Any |
408 | Upstream server timeout | Any |
413 | Remote file exceeds size limit | Any |
502 | Upstream server returned invalid response | Any |
Data Namespace Contracts
identity
{
"identity": {
"url": "https://youtu.be/GLTTI1NFHus?si=...", // original input URL
"finalUrl": "https://youtu.be/GLTTI1NFHus?si=...", // after redirect resolution
"canonicalUrl": "https://www.youtube.com/watch?v=GLTTI1NFHus", // normalized form
"mime": "text/html" // detected MIME type
}
}semantic
{
"semantic": {
"kind": "embed-media", // "direct-media" | "embed-media" | "webpage"
"semanticType": "embedded-video", // category-specific type
"title": "Provider Native Title" // when available
}
}playback
{
"playback": {
"strategy": "iframe", // "native" | "iframe"
"component": "iframe", // "video" | "audio" | "iframe" | "img"
"embedUrl": "https://...", // for iframe strategy
"streamable": false, // range request support
"downloadable": false // direct download capability
}
}metadata
Contains a filename, size, and exactly one category-specific sub-object:
// Image
{ "metadata": { "filename": "...", "size": { "bytes": 2048, "formatted": "2 KB" }, "image": { "colorSpace": "srgb", "hasAlpha": false } } }
// Video
{ "metadata": { "filename": "...", "size": { "bytes": 56733664, "formatted": "54.1 MB" }, "video": { "duration": { "seconds": 30, "formatted": "0:30" }, "fps": 24, "bitrate": { "bps": 1000000, "formatted": "1 Mbps" } } } }
// Audio
{ "metadata": { "filename": "...", "size": { "bytes": 1024, "formatted": "1 KB" }, "audio": { "duration": { "seconds": 30, "formatted": "0:30" }, "bitrate": { "bps": 128000, "formatted": "128 kbps" }, "sampleRate": 44100 } } }Cross-category metadata namespaces are always omitted — video never has metadata.image, audio never has metadata.video.