Back to Home
February 28, 20265 min read

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.

The Idempotency Pattern

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).

state_check.js (n8n snippet)
// 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.