Video evaluation is in preview. The
video request field is not yet shown in
the Create a new job reference,
so this page documents it. Request and response shapes may change before
general availability.How it Works
- Create a job for the video with
POST /v1/jobs. The response returns ajobUuidstraight away, with statusJOB_STATUS_PENDING. - Wait for the job to finish. It moves through
JOB_STATUS_RUNNINGtoJOB_STATUS_COMPLETEDorJOB_STATUS_FAILED. Get notified with a webhook, or poll. - Read the label hits with
GET /v1/jobs/{jobUuid}/labels. Each hit carries the label, its score, and where it happened in the video.
jobUuid). The gRPC and protobuf names are the snake_case equivalents
(job_uuid). In JSON, 64-bit integer fields such as startMs are encoded as
strings.
HTTP/JSON responses include every top-level field, even when it has no value:
an empty string is "", an empty list is [], and an unset object is null.
Check values, not whether a key is present.
Create a Video Job
Put avideo object in a contentData entry. The video must be hosted at a
publicly accessible HTTPS URL. Moonbounce downloads it from that URL, and you
can’t upload the file bytes directly.
The response is the job, still pending. Save
job.jobUuid.
Video requirements
- One video per request. A video can’t be combined with other content in the same job. To evaluate several videos, create a job for each one.
-
Use
POST /v1/jobs. The streaming endpoint (POST /v1/jobs/stream) doesn’t accept video. -
Formats. The URL must serve one of these content types:
video/mp4orapplication/mp4video/webmvideo/quicktimevideo/x-matroskavideo/x-msvideovideo/mpegorvideo/MP2Tvideo/3gpporvideo/3gpp2video/oggvideo/x-flv
application/vnd.apple.mpegurlorapplication/x-mpegurl. Moonbounce decides by theContent-Typeheader, not the.m3u8extension, so a playlist served asbinary/octet-streamortext/plainfails. The playlist must be video-on-demand (not live), unencrypted, and 3 hours or shorter. - Size. Up to 5 GiB.
-
Time. Downloading a video file can take up to 15 minutes. Retrieving
and assembling an HLS stream can take up to 60 minutes. If a job is still
running after 2 hours, it fails with
JOB_ERROR_REASON_EXECUTION_DEADLINE_EXCEEDED.
POST /v1/jobs. See Errors.
Know When It’s Done
Webhook
If you setwebhook.url, Moonbounce sends a POST to that URL when the job
completes or fails. That includes jobs that fail a precheck, where
POST /v1/jobs itself returned an error (see Rejected requests),
so you can receive a webhook for a job_id you never got back from the API. The URL must be HTTPS. Moonbounce also sends any
webhook.extraHeaders you supply, adding an X-Webhook- prefix to each header
name so it can’t collide with standard headers.
The body names the job and its status, and includes the job itself:
job object here is shortened. The top-level status is a short
lowercase value (completed or failed). The job.status inside it uses the
same JOB_STATUS_* values as the rest of the API.
The webhook’s job is not encoded the same way as the
GET /v1/jobs/{jobUuid} response. It leaves out fields that are unset or have
default values, and it doesn’t include assertion reports. Don’t parse both
with code that expects the same keys.
Polling
PollGET /v1/jobs/{jobUuid}, or call
GET /v1/jobs/{jobUuid}/labels directly. Both return the job’s current
status. Use an increasing interval between polls. Processing takes longer the
longer the video is.
Read the Label Hits
status. This
response was requested with pageSize=3:
Label hits appear only once the job is
JOB_STATUS_COMPLETED. If you call
this endpoint before then, it returns the current status with an empty
labels list, so you can use it to poll.How a video is evaluated
Moonbounce splits the video into scenes. Each scene is a frame segment, running from one detected frame to the next. Where there is speech, Moonbounce pairs the frame with the overlapping chunk of the audio transcript and evaluates the two together, so the model sees what is shown and what is said at the same time. Scenes without speech are evaluated from the frame alone. Every hit from a video job is reported asMODALITY_VIDEO, including hits that the transcript
contributed to.
A hit’s own range (startMs to endMs) is the part of the video where the
frame segment and the transcript chunk overlap. For example:
- The frame segment runs
[6000, 15000)ms. - The transcript chunk runs
[0, 10000)ms. - The hit’s range is their overlap,
[6000, 10000)ms.
frameStartMs, each hit with its own startMs/endMs. Don’t deduplicate
hits by frame, or you’ll drop real hits.
In the response above, the transcript chunk ([31, 152210) ms) contains each
frame segment, so each hit’s range equals its frame segment. In a scene without
speech, the hit’s range is also the whole frame segment, and the transcript
fields are absent.
Label hit fields
Hit fields that have no value are left out of the hit; they are never sent as
null. This applies only to fields inside a hit. The response’s top-level
fields are always present (see above).
Video jobs processed before paired scene evaluation was introduced keep their
original results. In those jobs, frame hits are
MODALITY_VIDEO and
transcript hits are reported separately as MODALITY_AUDIO. A frame hit may
have only startMs, and the frame* and transcript* fields are absent.
Code that reads older jobs should handle both shapes.Ordering and pagination
Hits are returned sorted bystartMs, then by label. Responses are limited to
1,000 hits. When more remain, nextPageToken is set. On the last page it is
an empty string ("").
To read every hit, repeat the request with
pageToken set to the last
nextPageToken, until nextPageToken comes back empty. Check for an empty
value, not a missing key: the key is always there, and sending an empty
pageToken fetches the first page again.
- Tokens are tied to the request. A
pageTokenworks only with the samejobUuid(and the samejobResultId, if you set one). - Only completed jobs are paginated. A job that hasn’t completed returns an
empty
nextPageToken. Sending apageTokenfor it is rejected. - Tokens don’t expire. A completed job’s hits never change, so you can resume reading later.
Errors
Rejected requests
These come back straight away fromPOST /v1/jobs:
A recorded job appears in
GET /v1/jobs as
JOB_STATUS_FAILED, but the error response doesn’t include its jobUuid. If
you set a webhook, it is called for a precheck failure, but not for a quota
failure or a failure to start. Don’t assume that an error from POST /v1/jobs
means no job exists.
Failed jobs
Problems found while the job runs mark itJOB_STATUS_FAILED and fill in
error on the job. For any other status, error is null in
GET /v1/jobs/{jobUuid} and GET /v1/jobs/{jobUuid}/labels responses, so
check status rather than whether error is present. You can read it from GET /v1/jobs/{jobUuid},
GET /v1/jobs/{jobUuid}/labels, or the webhook body:
reason. The message is fixed explanatory text for that reason. It
is safe to show to users, but don’t parse it.
New reasons may be added over time. Handle values you don’t recognize as a
general failure.
Reading label hits
A
jobResultId that isn’t part of the job is only reported once the job has
completed. Until then the call returns 200 with the current status, so a poll
with the wrong ID looks healthy.
Where to Next
Create a new job
The full
POST /v1/jobs request and response schema.Get job label hits
The
GET /v1/jobs/{jobUuid}/labels reference.Get job details
Read a job’s status, results, and failure details.
Submit job feedback
Tell Moonbounce whether a hit on a frame or transcript chunk was right.
We’d Love to Hear From You
Whether you have a suggestion, feedback, or a bug to report, here are the best ways to get in touch:- In the App: Use the Feedback button for direct suggestions.
- On Slack: Reach out to the team in your shared channel.
- With your AM: Talk to your dedicated account manager.
- Via Email: Send a message to support@moonbounce.io.
- Security, availability, or other incidents: Use the in-app Feedback button or email support@moonbounce.io. See Customer Feedback for what to include.