Every Reference the Model Takes, Over the Wire
A video model will take more than a prompt. Some take a first frame and a last frame. Some take a handful of reference images. Some take a reference clip, and then behave differently because of it — muting their own soun…
A video model will take more than a prompt. Some take a first frame and a last frame. Some take a handful of reference images. Some take a reference clip, and then behave differently because of it — muting their own soundtrack, or taking the clip's length as the output's length. Some take an audio clip. Every model draws that envelope differently, and most of ours were only sending part of it.
There is now one place that describes each model's envelope: how many images, whether a clip is allowed and for how many seconds, whether audio is accepted, whether audio needs a picture beside it, whether a clip mutes the model's own sound, whether a clip fixes the duration. It was read from the providers' own documentation and then exercised the same day with real renders on real keys, which is how both of the following were found.
A file with no extension
Handing a model a file by link is the obvious way to pass a clip. It fails on several of them, in a way that is very hard to guess: the file arrives with no extension, and the model validates by extension, so it refuses a perfectly good mp4 for not being an mp4. The same bytes sent inline were accepted everywhere. So every clip is re-encoded to a standard format first and sent inline, with a size ceiling above which the link path is used anyway and warned about.
A tier nobody was getting
The second one had been costing customers money. One of the video models has no resolution field at all — its quality tier is set through a differently-named field — and the platform in front of it silently discards fields it does not recognise. So every render on that model since it was added came back at the default tier while the customer had paid for the higher one. Nothing errored. The video arrived. It was just not the video that was billed.
The tier is now derived from the resolution that was actually priced, and confirmed by probe.
Seedance moves to a direct rail
In the same stretch, the whole Seedance family moved from the aggregator to the vendor's own API, with the aggregator kept as the fallback. One model row, one name, two sets of rates — and the rate you were quoted is the rate you pay even when the primary rail fails and the fallback runs, with the ledger recording which rail actually did the work so our own margin accounting reads the truth rather than the quote.
One thing to know as a user: when you attach a reference clip on that rail, the vendor bills the clip's seconds on top of the output's seconds. The estimate on the form says so, and the number quoted before you run is the number that is charged.
Found on the way
Three API clients were missing the one attribute that makes a vendor-named class visible to our container. Two of them were text-to-speech clients, invisible since the day they were added — which is why two voices that were seeded in the catalogue could never actually be served. A reflection test now fails the build on the next omission rather than waiting for someone to notice a voice that does nothing.
And the web search page we had shipped the day before answered 403 on every tier, on web and mobile alike. It is gated on a parent permission; both role seeders granted only that permission's three children; nothing infers a parent from its children. Four other features had each patched this by hand at some point. There is now a check that walks the permission tree and fails when a service is gated on something no role holds.