Skip to main content
Build a small app that uploads a video from your computer to Interhuman and returns structured analysis results. In this codealong, you will:
  • submit a local video to POST /v2/upload/analyze, which creates an analysis job
  • poll the job until it finishes, and show a processing state while you wait
  • optionally request a whole-file signal list and conversation-quality outputs
  • render each window’s engagement_status and signals[] (including per-signal rationale) and the quality metrics in your UI
This page is written so you can follow the build without the video, while the video helps with pacing and implementation details.

Watch the codealong video

Keep the video and your editor side by side; pause after each major step.
The video and the reference implementation were built against the earlier V1 upload flow: a single POST /v1/upload/analyze request that returns the analysis directly. The video also shows a legacy token-exchange step. Use them for the app’s structure and UI, and follow this page for the API calls. It describes the current flow: your API key in Authorization: Bearer <api_key>, a V2 upload job, and the V2 result shape. If you’re comparing against the V1 code, see Migrate from V1 to V2.
Reference implementation (V1 upload flow): interhuman-video-analyzer-codealong on GitHub

Build steps

1) Prepare credentials and auth

Before calling upload, make sure you have an API key from platform.interhuman.ai. If needed, follow Get an API key. The key needs the interhumanai.upload scope.

2) Add a simple file-upload UI

Create a minimal UI with:
  • a file input that accepts a local video file in mp4, mov, avi, mkv, webm, or mpeg-ts format
  • an Analyze button
  • a processing indicator, shown from the moment the user clicks Analyze until the job finishes
  • a results panel (for JSON output or parsed cards)
For this codealong, keep it simple. A plain HTML input and a single action button are enough to validate your integration end to end. The file must have both a video track and an audio track, because the inter-2 model analyzes sound as well as the picture. A file without a video track is refused with ih5001, and a file without an audio track with ih5008. The file must also be at least 3 seconds long (ih4007), no longer than 30 minutes (ih4004), and at most 32 MB (ih4003). An unsupported format is refused with ih4002. Show any error the submit returns in the results panel, so users know to pick another file.

3) Submit the video and wait for the job

When the user clicks Analyze, send the selected file to:
  • POST /v2/upload/analyze
  • with Authorization: Bearer <api_key>
  • as multipart form data, with the file in file and model set to inter-2
The API answers 202 with a job envelope: a job_id, a status of queued, and a status_url. Show the processing indicator, then poll GET /v2/upload/jobs/{job_id} every few seconds until status is completed or failed. The status moves through queued and running first. status_url is a path relative to https://api.interhuman.ai. When you build the polling loop:
  • Send the same Authorization: Bearer <api_key> header on each poll.
  • If a submit or a poll answers 503 (ih1002 or ih1003), wait a few seconds and retry it.
  • A job stays readable for 1 hour after submission (its expires_at). After that, reading it returns 404 (ih4021). Give the loop a deadline well inside that hour, and show an error if it runs out.
To skip polling for short files, add wait_seconds (up to 120) to the submit. The API then holds the request open and answers 200 with the job if it reaches completed or failed in time, so check status either way. If it doesn’t finish in time, you get the usual 202, so keep the polling loop. When the job is completed, hide the processing indicator and render its result. A failed job carries an error in the standard error shape instead of a result, so show its message. Reference: Inter-2 and Inter-2 Audio Upload Analyze

4) Optionally request more outputs

The job returns per-window analysis by default. You can request more with include[] flags on the submit:
  • signals adds one signal list over the whole file, in result.signals, with one entry per continuous span of each signal type.
  • conversation_quality_overall adds a single overall view, in result.conversation_quality.overall.
  • conversation_quality_timeline adds a timeline, in result.conversation_quality.timeline.
Each conversation-quality section is present only when you send its flag, so send both to get both. References:

5) Render results in the app

Once the job completes, render its result in your results panel. At minimum, show:
  • result.windows[]: each window’s span (start_seconds, end_seconds) and engagement_status
  • each window’s signals[] entries (type, probability, rationale)
  • optional result.signals, which is easier to show as a single timeline
  • optional result.conversation_quality.overall
  • optional result.conversation_quality.timeline[]
probability and rationale can be absent or null on a signal, so render them only when they have a value. This gives users both event-level insight (what happened and why) and quality-level insight (how the interaction performed across time).

What to inspect in the result

  • Windows: the result divides the file into fixed-length windows (window_seconds), in file order. engagement_status is engaged, neutral, or disengaged for each one.
  • Signal timing: a window signal’s start and end are its window’s span. In result.signals, an entry spans every adjacent window the signal appears in. All times are in seconds from the beginning of the uploaded video.
  • Probability: confidence bucket for each detected signal.
  • Rationale: a structured explanation of observed behavior (for example speech pauses, tone shifts, gaze, and pacing cues).
  • Conversation quality: overall plus core dimensions (quality_index, energy, rapport, authority, learning, clarity), and optionally a timeline that shows change over time.

Next steps