Running LinkedIn automation without losing your mind
n8n, idempotency, and state in Supabase—patterns from ZabeSync.
Problem
Automation workflows can become brittle when rate limits, retries, and idempotency are not handled well.
Solution
Built n8n workflows with state tracking in Supabase, explicit retry strategy, and controlled publishing paths.
Impact
Content workflow became stable and repeatable, with less manual intervention and better output consistency.
When you string together multiple APIs—an LLM, Discord, LinkedIn, and Google Drive—things will break. APIs rate limit you, webhooks drop, and LLMs sometimes return malformed JSON. Without idempotency, a single failure can lead to embarrassing double-posts or lost content.
The Brittleness of Stateless Automation
My first iteration of the ZabeSync pipeline was a straightforward n8n workflow. It worked perfectly in testing. But in production, if the LinkedIn API threw a 502 error during posting, the workflow would fail. When I hit retry, it would re-research the topic, re-generate the post, and try again. Not only was this wasting LLM tokens, but the resulting post was completely different from the one I had approved via Discord.
An operation is idempotent if running it once has the same effect as running it multiple times. For our pipeline, this meant separating state from execution.
Introducing Supabase for State
To fix this, I introduced Supabase as a state management layer. Every idea generated got a unique ID and a status (DRAFT, APPROVED, PUBLISHED).
// Before attempting to publish, verify state
const postId = $json.postId;
const { data, error } = await supabase
.from('content_pipeline')
.select('status')
.eq('id', postId)
.single();
if (data.status === 'PUBLISHED') {
// Gracefully exit, already published
return { success: true, skipped: true, reason: 'Already published' };
}
// Proceed to publish to LinkedIn...Handling Rate Limits and Retries
Instead of relying on the trigger system to handle retries, I implemented explicit sub-workflows in n8n. If the Google Custom Search API returned a 429 Too Many Requests, the workflow would pause for a dynamic backoff period (calculated based on the retry attempt) and try again.
- Discord as the Control Plane
By moving the approval process to interactive Discord buttons, humans remain in the loop without needing to log into n8n or a custom dashboard.