Skip to main content
This page is the protocol reference for Pika H3 Flex: its tracks, commands, refusals, and messages. The model streams video on main_video and audio on main_audio. Send commands with reactor.sendCommand() once the session reaches ready.

Tracks

There are no input tracks. The model takes everything it needs from commands. Both tracks stream only while a clip plays. Between clips, main_video shows black and main_audio is silent. Turn off set_flush_on_clip_end to hold the last frame instead of black. Each session starts at 16:9 and the 768p resolution, which is 1344 × 768 pixels. set_resolution switches between 768p and 480p (832 × 480 at 16:9), and set_canvas changes the aspect ratio. Call them while both queues are empty and nothing plays, for example before you queue the first clip. At any other time the size stays fixed. state_update, canvas_accepted, and resolution_accepted report the size in pixels.

Session lifecycle

A Pika H3 Flex session has two queues:
  • The generation queue holds clips that wait to generate.
  • The playout queue holds clips that are ready to play.
When a clip finishes generating, it moves to the back of the playout queue. Playing a clip takes it out of the playout queue and onto the output tracks. Nothing plays until you call play, unless autoplay is on. When you connect, the model sends you a state_update and a queue_update. A new session has both queues empty, autoplay off, and nothing playing. state_update.valid_commands lists the commands the session accepts right now. Use it to turn controls on and off in your UI. Generation keeps going while a clip plays, so you can queue the next clip during playback. It runs only while at least one client is connected. Several clients can connect to one session and share its queues and stream. A command from any client changes the state for all of them. Replies go only to the client that sent the command, while state, queue, and playback messages go to every client. reset stops playback, clears both queues and the clips you could continue from, clears the tracks, and restores the defaults.

Commands

The typed SDK, @reactor-models/pika-h3-flex, has one method for each command on PikaH3FlexModel and on the usePikaH3Flex() hook. play and stop resolve undefined. The other methods resolve their reply message, or undefined if the model refuses the command. Parameter and message fields keep their snake_case names. Pass the FileRef that uploadFile returns for each uploaded image, video, or audio clip.

enqueue

Add a clip to the generation queue. The model replies with clip_queued when it accepts the request. Later it sends clip_generated when the clip is ready, or clip_failed if generation fails. The inputs decide how the model builds the clip. There is no field that chooses a mode: Frame images and references never share a clip. The model refuses a request that combines them with invalid_request, and it also refuses a frame continuation that has references or frame images.

References

Reference images guide how subjects, objects, and settings look. Reference videos guide motion, appearance, and sound. Reference audio guides voices and sound. None of them fixes the clip’s first or last frame. A clip takes up to 9 images, 3 videos, and 3 audio clips, and at most 12 references in total. A request with reference audio must also include a reference image, a reference video, or a latent continuation, or the model refuses it with invalid_request. Each clip keeps its own references, and a later clip does not reuse them. Upload each file with uploadFile() and pass the FileRef it returns. The same file uploaded twice counts as two references with two labels.

Labels

In the prompt, <Picture N> names the Nth image and <Video N> the Nth video, in the order of their own arrays. Audio clips and the sound of reference videos share the <Audio N> numbers, in packed order. A video with sound takes the next audio label just before its own place. With the default order, one image, one video with sound, and one audio clip are named like this: If the video has no sound, the audio clip is <Audio 1>. If you change reference_order, the labels change too, so update the prompt to match.

Reference videos

Reference videos are experimental. Each video is 2 to 15 seconds long, at most 64 MiB, and has an aspect ratio between 1:4 and 4:1. The model samples each video at 24 frames per second, cuts it to the clip’s length, and fits it to its own aspect ratio at the session’s resolution. If the video has sound, the sound guides the clip too. The model refuses a video that does not decode or is out of range with invalid_request.

First and last frames

first_frame_image sets how the clip opens, and last_frame_image sets how it ends. Send one or both. The model center-crops each image and resizes it to the canvas. It rebuilds the image rather than pasting it, so the first or last frame is close to the image but not a pixel copy. Describe the action between the two frames in the prompt. In the prompt, a single frame image is <Picture 1>. With both, the first frame is <Picture 1> and the last frame is <Picture 2>. The prompt guide shows the alignment line Pika recommends.

Continuation

continue_from_clip_id continues a clip from this session, whichever way it was built. continue_mode says how: In a latent continuation, the end of the source clip is packed last, as a video reference. Its sound takes the next audio label after all your other audio labels, but it does not use an audio slot. You can still upload 3 audio clips: with three uploads and no reference video, the continued sound is <Audio 4>. The end of the source clip does count as one of the 3 reference videos, so a latent continuation accepts at most 2 reference videos, at most 12.67 seconds together. Wait for the source clip’s clip_generated before you queue a latent continuation. You can queue a frame continuation right away, even before its source has generated. It waits in the generation queue until its source is built, and the clips behind it keep generating. queue_update.history lists the built clips that left both queues. You can no longer play them, but you can continue them. reset and the end of the session remove them.

Lengths

seconds and set_clip_seconds accept 25 values: 5, 5.167, 5.875, 6, 6.583, 7, 7.292, 8, 8.708, 9, 9.417, 10, 10.125, 10.833, 11, 11.542, 12, 12.25, 12.958, 13, 13.667, 14, 14.375, 15, and 15.083 seconds. The whole numbers are shortcuts: each one gives the shortest generated length at or above it, so 5 gives 5.167 seconds, 8 gives exactly 8, and 15 gives 15.083. clip.seconds reports the length the clip actually has. The model refuses any other value and does not round it. Read the list from state_update.clip_seconds_choices rather than hard-coding it.

Limits

The model checks each request before it accepts it, and Refusals lists the codes. Each image can have at most 25 million pixels, with an aspect ratio between 1:4 and 4:1. Each upload can be at most 64 MiB, and a clip’s uploads at most 128 MiB together. The model refuses any field it does not take with invalid_request.
To continue that clip, wait for its clip_generated message and keep its clip.clip_id, then enqueue the next clip with that ID. This request uploads one audio clip, so the continued clip’s soundtrack becomes <Audio 2>:
To build a clip between two images, send the frame images and no references. This prompt follows Pika’s alignment line for an 8-second clip:
A frame continuation opens on the last frame of another clip and takes a prompt only. The source clip’s last frame is <Picture 1>:

play

Play a ready clip. Leave out clip_id to play the clip at the front of the playout queue. The model sends clip_started when the first frames reach the tracks and clip_finished when the clip ends. A clip plays only once, and you cannot play it again afterward. You can continue from a clip in queue_update.history, but you cannot play it.

stop

Cut the playing clip and leave the queues as they are. With autoplay on, stop skips to the next ready clip. With flush on, the video cuts to black. With flush off, it holds the last frame once the stream drains.

pop and move

Session settings

state_update carries the current defaults: clip_seconds is the current default length, and clip_seconds_choices lists the allowed lengths. resolution is the current resolution, and resolution_choices lists the ones you can set.
set_seed changes the seed for later clips. set_flush_on_clip_end takes effect at the next clip boundary. get_state and get_queue reply with the same payload that the model broadcasts. reset starts the session again from empty:

Refusals

When the model refuses a command, nothing changes and the call fails. Only the client that sent the command gets the error. It carries a stable code and a reason you can show to your user.
  • request_rejected: the prompt is over the 2,048-token budget. The reason gives the count and the limit.
  • invalid_image: a reference image or frame image does not decode.
  • invalid_audio: an audio clip does not decode or is outside 2 to 15 seconds. This code also applies when the audio clips last more than 15 seconds together.
  • invalid_request: the request breaks a rule that is not about one file. For example:
    • a length that is not offered
    • too many references: more than 9 images, 3 videos, or 3 audio clips, or more than 12 in all
    • frame images together with references, or a frame continuation with references or frame images
    • reference audio without an image, a video, or a latent continuation
    • a reference video that does not decode or is out of range, or videos that last more than 15 seconds together (the end of a latent-continued clip counts as 2.33 seconds)
    • a reference_order that does not name each upload once
    • a continuation ID that the model does not hold, or continue_mode without a continuation
    • an upload over the size limits, or a field that this model does not take
    • a canvas or resolution change while the model prepares the request
  • no_ready_clip: the ID is unknown, the clip is still generating, or the playout queue is empty.
  • has_dependents: queued clips continue from this clip, and it has not finished generating.
  • session_not_empty: a clip is queued or playing, so the size cannot change.
In JavaScript, the error event fires with operation: "sendCommand" and the code, and then the awaited call resolves undefined. Commands that declare a reply resolve undefined only when the model refuses them. play and stop declare no reply and resolve undefined on success too, so for these two only the error event shows a refusal. In Python, send_command() raises a ReactorError with the code.
The model also broadcasts a command_error message to every client when it refuses a command. That broadcast is legacy. Read the refusal from the call instead. See Replies and refusals.

Messages

A failed clip leaves the generation queue, and generation moves on to the next clip. In clip_finished and clip_stopped, seconds_sent is the total for the session, not for one clip.

ClipInfo

Every message about a clip carries the full ClipInfo record. The first seven fields are always present. The others are optional.

state_update

queue_update

Complete example

This example queues a clip from a prompt and plays it when it is ready. The tutorial adds references and continuation.