If you integrate more than one social platform's API, you already know each provider handles tokens differently. Some expire in hours. Some refresh silently in the background. Some revoke access the moment a user changes a password, with no webhook to tell you.
None of that is the hard part. The hard part is what happens next.
Publish time is the worst possible moment to find out
Say a post is scheduled for 9am. Your job runs, calls the API, and gets a 401. The token is dead.
From the user's side, this looks identical to "the tool stopped working." They don't see an auth error. They see a missing post. By the time they notice, the slot is gone, and there's no way to get it back. You can retry a rate limit. You cannot retry a scheduled tweet from three hours ago.
So the failure isn't really a technical one. It's a timing one. You found out about a fixable problem at the one moment you could no longer fix it.
The fix: treat the connection as a first-class object
The decision that actually matters here is small: stop treating "post to platform X" as a single step, and start treating the connection itself as something with a health state.
Something like this:
Connection {
id
platform
status: "healthy" | "needs_reconnect" | "revoked"
last_checked_at
refresh_token
}
Before a scheduled job runs, it checks the connection's status, not just whether it has a token:
function preflightCheck(connection) {
if (connection.status === "revoked") {
return "needs_reconnect" // user has to act
}
if (isExpiringSoon(connection)) {
const refreshed = tryRefresh(connection)
if (!refreshed) {
connection.status = "needs_reconnect"
return "needs_reconnect"
}
}
return "healthy"
}
If the check fails, the queued post moves to a needs_reconnect state instead of just vanishing. That state is visible to the user, ideally days before the scheduled slot, not after.
Two error classes, not one
This only works if you separate two things that look the same in a stack trace but require completely different fixes:
A token that's simply stale. Your backend can refresh it programmatically, no user involved. This should never surface as an error at all.
A permission the user revoked. Nothing on your side can restore this. Only the user, going back through the provider's consent screen, can fix it.
Collapsing these into one generic "auth failed" state is the actual bug. The first case should be invisible. The second case needs a clear, specific prompt: reconnect this account, here's why, here's the link.
The transferable takeaway
Any integration that fans out to multiple platforms needs a way to surface "reconnect this account" before the deadline, not after. That means checking connection health on a schedule, separately from running the job that depends on it, and giving the user enough lead time to actually act.
We built this into Cadencz because it schedules and publishes across 11 platforms through connected accounts, and the backend owns every OAuth callback rather than the frontend, so connection state is centralized in one place instead of scattered across per-platform request code.
If you're integrating even two or three platforms, the same principle holds: a dead token is not a publish-time problem. It's a state you should already know about.
Top comments (0)