Negotiate the Format Before You Pay the Vendor

The advice everywhere is to validate input early. It is good advice and it is not enough, because it does not tell you what "invalid" means when the answer is a matter of degree.
An aspect ratio is never simply wrong. A 1080×1920 target against a 1080×1918 source is off by a tenth of a percent and nobody will ever see it. The same target against a 1024×1024 source is off by 44% and the result is a different video than the one that was requested. Somewhere between those two is a line, and where you draw it decides whether your API refuses work before a vendor charges you or apologises for a bill that already landed.
Here is where we drew it, and what we got wrong on the way. The adjacent rule, that an unmet parameter comes back named, is what makes the middle band survivable at all.
Three bands, not two
Validation usually produces a boolean. This produces three outcomes.
Within 2%: treat it as a match. No warning, no adjustment worth mentioning. The difference is smaller than the rounding the encoder does anyway. Warning about it would train clients to ignore warnings, which is the real cost of a chatty API. No standard sets this number for you: an aspect ratio is an exact rational everywhere it is specified, so the tolerance is a product decision and nobody else can make it.
Between 2% and 15%: adjust it, and say what the vendor will do. This is the interesting band. The result is recognisably the video that was asked for, the adjustment is real, and the client needs to know it happened.
Above 15%: refuse, before the paid call. Not after. The ordering is the whole point of this post.
The part we got wrong: the warning has to name the direction
Our first version of the warning said the source would be centre-cropped. That is true exactly half the time.
Crop happens when the target is narrower than the source. A square image into a vertical frame loses the sides. But when the target is wider than the source, nothing is cropped at all. The frame gets padded, and the client sees white bars where they expected picture.
Both are adjustments. Both deserve a warning. They are opposite outcomes, and a client reading "centre-crop" while looking at letterboxing has been told something false by an API that was trying to be helpful. So the warning names what the vendor will actually do to this particular pair of dimensions, in this direction.
This is a small correction and a general lesson: a warning that describes the usual case is a warning that lies in the unusual one, and the unusual one is exactly when someone is reading warnings.
Strictness scales with specificity
The 15% refusal applies only to a format the client explicitly asked for.
If the request carries no aspect ratio, there is nothing to violate. Any mismatch adjusts, with a warning, however wide the gap. The client did not express a preference, so we take the sensible default and tell them what we did.
If the request does carry one, the client has made a claim about what they need, and quietly returning something 40% different is not a service. So we refuse.
The same input produces a refusal or an adjustment depending on whether anyone asked for something specific. That felt inconsistent when we wrote it and obviously right once it shipped: the strength of a constraint should match the strength of the request. An unstated preference cannot be violated. A stated one can.
Why "before the paid call" is doing the work
A vendor call costs money. Once it has been made, refusing is no longer free. You are choosing between charging for something the client did not want and eating the cost of work you dispatched on their behalf.
That decision is genuinely hard, and the cheapest way to win it is never to arrive there. Every check that can run before the vendor is paid should run before the vendor is paid, even when running it later would be simpler to implement. This is the ordering behind the four billing outcomes.
We were tempted to defer it. Measuring the source image means fetching it, and fetching it means a network round trip before you have done anything useful. It is faster to dispatch the job and sort out the geometry from whatever comes back. That version is easier to write and it converts a validation problem into a billing problem, which is a much worse problem to have. We wrote about what delivery actually authorises separately, because the billing side needed its own rules.
Picking your own numbers
Do not copy 2% and 15%. They come from what our vendor does to a frame and what our clients produce, and both of those are ours.
What transfers is the method:
- The lower bound is where the difference stops being visible. Find it empirically: render the same job at increasing mismatch until someone can tell. Below that, silence.
- The upper bound is where the output stops being the thing that was requested. Above it you are not adjusting a video, you are substituting one.
- The middle band is the one that needs prose, and the prose has to describe the actual direction of the change rather than the common case.
If your lower and upper bounds turn out to be the same number, you have a boolean after all, and that is a legitimate finding. It means every mismatch in your domain is either invisible or fatal. Ours is not that domain.
FAQ
Why not let the client choose the tolerance?
We considered it and dropped it. A tolerance parameter pushes a decision onto someone with less information than us. The client knows what they want the video to look like, and we know what this vendor does to a frame at 12% mismatch. Exposing the knob mostly produces clients who set it to 100 and stop getting refusals, which is the outcome the refusal exists to prevent.
What if the source image is unreachable during the probe?
Then no measurement exists, and there is nothing to compare. The job proceeds on the client's stated format, because a failed probe is our problem and not a reason to refuse their work. It is also why the probe result is stored as a fact rather than folded into a warning string. A missing measurement and a measurement showing no mismatch are different states, and they need to stay different.
Does refusing hurt your success rate?
On paper, yes, and that is the correct trade. A refusal before the paid call is a job that did not happen and did not cost anyone anything. Counting it as a failure alongside genuine errors makes the dashboard look worse and the product behave better. We would rather explain the metric than explain a bill.
The crop you did not ask for costs the same as the one you did.
A gap too wide to render is refused before the vendor is paid, not after — and what we adjust comes back named. That is the contract behind Clipwright.
About the Author
Dimantika
Founder of Dimantika. Co-founded and exited a SaaS at $1.2M ARR. Now building AI tools for founders who want autonomous growth without blind trust in agents.
View all postsRelated posts
More articles you might like.

Never Answer an Agent With a Silent Substitution
An unmet parameter should come back named. We learned why the hard way: two warnings went missing for weeks and every test stayed green.

One Reviewer Approved It. The Other Found the Bug.
Six blockers across four consecutive stories, and the two reviews agreed on one of them. Here is what running both actually cost us, and the rule we ended up with.

Your Agent Can Branch on a Refusal. It Cannot Branch on a Warning.
Refusals in our API carry stable codes. Warnings carry none, on purpose — and the bug that proved why cost us two warnings nobody noticed were missing.