We don't rebuild
what we can rent.
And we never rent the judgement.
Publishing a video to nine networks is a solved problem. Someone else has already passed TikTok's app audit, already handles OAuth refresh, already absorbs every breaking API change. Paying them a monthly fee is not a compromise — it is the correct architecture.
What is not for rent is whether a piece of content should go out at all. That judgement — two gates, one of them across model families, one of them a human signing for the brand — stays ours. It is where the engineering actually went.
- Status
- Running daily
- Kind
- Content operations
- We own
- Content, gates, data
- We rent
- Platform distribution
- Review
- Two gates, cross-model
- Rollback
- One feature flag
- TikTok rent decisionThe hard part is the platform audit, not the HTTP call. A partner who already passed it sells that as a line item.
- YouTube build decisionStays on our own integration. The rented analytics are worse than the native ones.
- Instagram rent migratedMoved to the partner behind a feature flag. Setting the flag back to 0 restores the old path.
- Review claude-authored→reviewed by another family routingA model reviewing its own family's output agrees with itself too often.
- Review glm-authored→reviewed by a Claude model routingSame rule in the other direction. The table is symmetric on purpose.
- Rejected reviewer→human escalationTwice rejected means stop. The pipeline does not retry its way past a judgement it failed.
Each of these is a standing rule, not a one-off call. The routing table lives in one module so every agent that touches content resolves it the same way.
Brief Why integration is the harder skill § 01
The reflex in a shop like ours is to build.
Building is legible. It feels like progress, it demos well, and it produces something you can point at. That reflex is also how teams end up maintaining a worse copy of something they could have had for the price of a lunch.
Some things genuinely must not be built. A publishing integration for a large social platform is not really code — it is an audit relationship. The platform reviews the application that posts on your behalf, that review takes weeks, and it can simply fail. A partner who has already passed it turns a multi-week compliance project into a monthly bill and a URL in a request body.
So the interesting question was never build or buy. It was where the seam goes: what we keep, what we hand over, and how we get back if the rented thing disappoints us.
Ledger Where the seam sits § 02
The board every item moves across, with the columns the pipeline actually uses. Two of the five are human: one where a reviewer on a different model family has to pass it, one where a person signs for the brand. An item rejected twice stops there and waits — it is never quietly retried.
Decisions What we chose, and what it cost § 03
Four calls about where to stop building.
Rent the audit, not just the API.
We wrote our own publisher for one network and then stopped. The blocker was never the request — it was the review process behind it. So the newest channel runs through a partner that had already cleared that review, and we hand them a URL to a video we still host ourselves. We rent reach. We do not rent the file.
Where the rented option was worse we kept ours: the video platform's own API returns better performance data than the aggregator does, so that channel never migrated.
Cost: a dependency we do not control, and a recurring bill.
Bought: a working channel this week instead of next quarter — and no audit risk of our own.
The reviewer must not share the author's model.
A model reviewing output from its own family agrees with itself far too readily. So the routing table is explicit and symmetric: anything drafted by an agent on one model family is reviewed by an agent on a different one, in both directions. After that a department lead signs off for the brand. Content reaches a channel only when both gates have passed.
When a draft is rejected twice, the pipeline stops and raises it to a person. It does not retry its way past a judgement it already failed.
Cost: two model vendors to keep working, and a slower path from draft to published.
Bought: a second opinion that is genuinely second.
Channel status is derived, never stored.
The obvious design is a status column per channel. We compute it instead, from flags the row already carries. The database row stays the single source of truth and cannot contradict itself, and the board a human looks at is simply the answer to “what has to happen next”, worked out on read.
Cost: a little logic on every read, and a rule that nobody may cache it.
Bought: no second truth to migrate, and no drift between the two.
Only content that can declare itself honestly gets that channel.
One platform requires a brand-disclosure flag on every post. Setting it to false while publishing promotional material would be a lie told by a cron job, so eligibility is restricted by content type: helper, guide, tutorial, explainer, problem-solution and explicitly neutral comparisons can reach that channel. A product-favouring comparison is not quietly reworded to slip through — it is excluded.
Cost: fewer items eligible for the channel with the largest reach.
Bought: a declaration we can defend, in writing, to the platform and to a customer.
Cadence What the schedule looks like § 04
A process is only real if it survives a calendar.
Gates are easy to describe and hard to keep. This is a month of the publishing plan: what is booked, on which channel, and the two kinds of item that are deliberately not moving.
Solid means approved and dated. A dashed marker is an item the system will not publish on its own: either it is still being worked on, or it is waiting for a person. Nothing slips from one state to the next because a date arrived.
Incidents Two assumptions that turned out wrong § 05
Both were about a seam, not about a component.
Support integration
The ticket data was available. It was not reachable.
Support tickets already flowed in: a listener collected them and wrote daily files for the scheduled jobs. But anyone who wanted to look at one ticket right now had to assemble the call by hand — and the credential for it was only present inside the scheduled jobs. From an ordinary shell the endpoint answered with a flat refusal, so it looked like nobody had access. Everybody had access. Nobody could reach it.
What we had assumed: that data arriving somewhere counts as data being available. It does not. A pipeline that only serves its own cron is half a pipeline.
Fix → a small command that resolves the credential itself — and that imports the existing collector's transport code instead of reimplementing it. One integration, two ways in, one place where it can break.
Control granularity
One switch per product was the wrong axis entirely.
The first control model gave each product a switch for ads, video and blog. It was far too coarse — a single ads switch covering four different kinds of advertising, no concept of format, no concept of an individual account. The practical result was production on suspicion: things were made because a switch was on, not because a channel needed them.
What we had assumed: that the product was the axis worth controlling. The correction came out of the code — an upload guard already refused any upload whose brand did not match the target account. The missing layer was the brand, and it replaced most of a product-by-channel matrix.
Fix → three honest levels instead of one vague one: what a platform technically supports, which concrete account we are posting to, and which format we are making. Existing code told us the model was wrong before any human noticed.
Transfer What this means where you are § 06
Integration decisions age faster than code decisions. Make them explicit.
Rent the compliance, not just the code
When a vendor's real product is an approval you would otherwise have to earn yourself, the licence fee is buying a schedule, not a library. That is usually the strongest buy case there is.
Every rented thing needs a way back
We moved a channel to a partner behind a flag, and the old path stayed in place. An integration you cannot reverse in one move is not an integration, it is a migration you have not finished thinking about.
A second opinion needs a different model
Cross-family review costs a vendor relationship and some latency. It is the cheapest way we know to stop a fleet from confidently agreeing with itself all the way to a customer.