Haptica API
Physics perception from video. Send a clip, get structured physics signals back.
curl https://api.haptica.io/v1/analyze \ -H "Authorization: Bearer hp_live_..." \ -F "video=@clip.mp4"
{
"video_id": "a1b2c3d4e5...",
"provenance": {
"model_version": "v3.1",
"encoder": "haptica-physics",
"checkpoint": "v3.1-prod"
},
"input": {
"fps_source": 30,
"duration_ms": 4800,
"window_frames": 16
},
"availability": {
"force_index": "active",
"contact": "active",
"impact": "active",
"rotation": "active",
"bind_jam": { "status": "null", "reason": "no_context" },
"human_risk": { "status": "null", "reason": "no_context" }
},
"windows": [{
"index": 0,
"timing": { "start_ms": 0, "end_ms": 1600, "canonical_fps": 10.0 },
"physics": {
"force_index": 0.723,
"contact": { "detected": true, "confidence": 0.94 },
"impact": { "detected": false, "confidence": 0.12 },
"rotation": { "detected": true, "confidence": 0.87 },
"bind_jam": null,
"human_risk": null
},
"evidence": {
"dominant_primitive": "contact",
"dominant_candidate": "contact",
"activity_regime": "clear",
"dominant_margin": 0.07,
"stability_score": 0.72,
"tokens": { "contact": 0.94, "impact": 0.12, "rotation": 0.87 }
}
}]
}Authentication
All requests require a Bearer token in the Authorization header.
Authorization: Bearer hp_live_abc123def456...
API keys are prefixed with hp_live_. Keys are shown once at creation and stored as SHA-256 hashes. Treat them like passwords. Use POST /v1/demo/analyze (no auth) to test with pre-computed sample output.
POST /v1/analyze
Analyze a video clip for physical interactions. Returns per-window physics signals.
Content type: multipart/form-data. Max video size: 100 MB. Supported formats: mp4, webm, mov.
Parameters
| Parameter | Type | Description |
|---|---|---|
video | file | Video file upload (mp4, webm, mov, avi). Max 100 MB, max 60s. |
video_url | string | HTTPS URL pointing to a video file. Provide either video or video_url. |
context | string | Task context JSON. Activates configured channels (bind_jam, human_risk). |
Full example
import requests
resp = requests.post(
"https://api.haptica.io/v1/analyze",
headers={"Authorization": "Bearer hp_live_..."},
files={"video": open("clip.mp4", "rb")},
)
result = resp.json()
for w in result["windows"]:
t = w["timing"]
p = w["physics"]
print(f"[{t['start_ms']}–{t['end_ms']}ms] "
f"force={p['force_index']:.2f} "
f"contact={p['contact']['detected']} "
f"impact={p['impact']['detected']}")GET /v1/model/info
Returns current model version, supported formats, and available channels.
{
"model_version": "v3.1",
"encoder": "haptica-physics",
"checkpoint": "v3.1-prod",
"signals": {
"core": ["force_index", "contact", "impact", "rotation"],
"configured": ["bind_jam", "human_risk"]
},
"auroc": { "contact": 0.78, "impact": 0.72, "rotation": 0.71 },
"window_frames": 16,
"canonical_fps": 10.0,
"supported_formats": ["mp4", "mov", "avi", "webm"]
}POST /v1/stream
Real-time streaming analysis. Send video frames via WebSocket, receive physics signals as they are computed. Available on Scale and Enterprise tiers.
POST /v1/context
Create or update a context profile. Context profiles activate configured channels (bind_jam, human_risk) and improve prediction accuracy for specific task domains.
Response schema
Windowing
The model resamples every video to a 10 Hz canonical grid regardless of source frame rate. Windows are 16 frames (1600 ms) with a stride of 8 frames (800 ms), giving 50% overlap between consecutive windows.
This means window 0 covers 0–1600 ms, window 1 covers 800–2400 ms, window 2 covers 1600–3200 ms, and so on. The overlap is part of the contract — if you depend on it for temporal smoothing or interpolation, it is guaranteed to remain stable.
Window fields
Every Window object in the response contains these fields.
| Parameter | Type | Description |
|---|---|---|
index | int | Window index (0-based) |
timing.start_ms | int | Window start time in milliseconds |
timing.end_ms | int | Window end time in milliseconds |
timing.canonical_fps | float | Canonical frame rate (10 Hz). Windows are 1600 ms with 800 ms stride (50% overlap). |
physics.force_index | float [0, 1] | Continuous force intensity. Monotonic with measured Newtons. |
physics.contact | { detected, confidence } | Physical contact detection + confidence [0, 1] |
physics.impact | { detected, confidence } | Sudden force onset: collision, strike, drop |
physics.rotation | { detected, confidence } | Rotation under load: screw, turn, twist |
physics.bind_jam | object | null | Mechanical binding/jamming. Null without context. |
physics.human_risk | float | null | Context-aware safety risk [0, 1]. Null without context. |
evidence.dominant_primitive | string | null | Highest-confidence evidence channel. Null when activity_regime is not "clear". |
evidence.dominant_candidate | string | Highest-scoring channel (always present, for debugging) |
evidence.activity_regime | string | "clear" | "low_activity" | "ambiguous" — confidence regime classification |
evidence.dominant_margin | float | Gap between top two evidence channels [0, 1] |
evidence.stability_score | float | Prediction confidence/decisiveness [0, 1] |
evidence.tokens | object | { contact, impact, rotation } -- per-channel evidence [0, 1] |
Null semantics
Core signals (force_index, contact, impact, rotation, evidence) are always present and never null.
Configured signals (bind_jam, human_risk) return null unless activated by context. Check the top-level availability map for reason codes: no_context, unsupported_domain, calibration_required.
Null means unsupported for this request, not missing data. Do not retry on null.
Errors & rate limits
Error codes
| Code | Meaning | Details |
|---|---|---|
| 400 | Bad request | Invalid video format, missing required fields, unsupported content type, or invalid URL |
| 401 | Unauthorized | Missing or invalid API key, or key has been revoked |
| 413 | Payload too large | Video exceeds 100 MB size limit or 60s duration limit (video_too_long) |
| 429 | Rate limit exceeded | Check Retry-After header. Back off exponentially. |
| 500 | Internal error | Retry with exponential backoff. If persistent, contact support. |
Rate limits per tier
| Tier | Rate | Quota |
|---|---|---|
| Standard | 3 concurrent | 60s max duration |
| Trusted | 3 concurrent | 5 min max duration |
| Enterprise | Custom | Custom |
When rate-limited (429), the response includes a Retry-After header in seconds. Use exponential backoff with jitter.
Recipes
Robot safety monitoring
Detect dangerous force events and trigger safe stops.
import haptica
client = haptica.Client("hp_live_...")
result = client.analyze(video="robot_arm.mp4")
for w in result.windows:
p = w.physics
if p.force_index > 0.7 and p.contact.detected:
print(f"[{w.timing.start_ms}ms] HIGH FORCE CONTACT -- force={p.force_index:.2f}")
if p.human_risk and p.human_risk > 0.8:
print(f"[{w.timing.start_ms}ms] SAFETY ALERT -- risk={p.human_risk:.2f}")
robot.trigger_safe_stop()Warehouse anomaly detection
Flag unusual force patterns relative to baseline.
# Flag anomalous force patterns in warehouse footage
result = client.analyze(video=feed_url)
baseline = sum(w.physics.force_index for w in result.windows) / len(result.windows)
anomalies = [w for w in result.windows if w.physics.force_index > baseline * 2.5]
for w in anomalies:
alert(f"Anomaly at {w.timing.start_ms}ms: force {w.physics.force_index:.2f} (baseline {baseline:.2f})")Evidence-based thresholding
Use per-channel evidence scores for fine-grained decision making.
# Use evidence scores for fine-grained decision making
result = client.analyze(video="assembly.mp4")
for w in result.windows:
tokens = w.evidence.tokens
# High contact evidence but low impact = sustained press (normal)
if tokens["contact"] > 0.8 and tokens["impact"] < 0.2:
log("Sustained contact -- normal assembly pressure")
# High impact evidence = unexpected collision
if tokens["impact"] > 0.7:
log(f"Impact at {w.timing.start_ms}ms (evidence: {tokens['impact']:.2f})")
queue_review(w)Agent integration
Drop this tool schema into your agent framework (OpenAI function-calling format).
{
"type": "function",
"function": {
"name": "haptica_analyze",
"description": "Analyze video for physical interactions. Returns force, contact, impact, rotation, and evidence scores per 1.6s window.",
"parameters": {
"type": "object",
"properties": {
"video": {
"type": "string",
"description": "URL or base64-encoded video clip"
},
"context": {
"type": "string",
"description": "Task context string (e.g. 'manufacturing_assembly')"
}
},
"required": ["video"]
}
}
}Machine-readable resources
For automated tooling and LLM integration.
haptica.io