Inspiration

Walk into any animal shelter and you'll find the same scene: dedicated staff stretched impossibly thin, juggling feeding schedules, medical care, adoptions, and — somewhere at the bottom of the to-do list — social media.

A shelter with 80 animals might post 3 or 4 a week, not because they don't care, but because writing a compelling caption, resizing a photo, logging into three different platforms, and repeating that process for every pet is genuinely exhausting work.

The math is stark. If a shelter has 100 animals and can only post 4 per week, it takes over 6 months to give every pet a single moment of visibility.

And that's assuming no new animals come in. In reality, the queue never clears.

I built PawPost because I believed that number should be close to zero — every pet, every platform, every day, with almost no manual effort.


What it does

PawPost is a multi-tenant social media automation platform built specifically for animal shelters.

Staff upload a pet photo and Claude AI instantly extracts the animal's species, breed, approximate age, gender, and personality traits directly from the image. It then generates a ready-to-post adoption caption (≤295 characters) in the shelter's chosen tone — warm and hopeful, playful and fun, professional, or urgent.

With one click, the post publishes simultaneously to Bluesky, Facebook, and Instagram.

Shelters can also:

  • Schedule posts for specific times
  • Manage a full posting queue with search and filters
  • Review adoption inquiries from potential adopters

All from a single dashboard.

Each shelter is a fully isolated tenant with its own staff accounts, pet profiles, social credentials, and posting schedule.


How I built it

Frontend: React + TypeScript + Vite, deployed on Netlify
Backend: Node.js + Express + TypeScript, deployed on Railway
Database, Auth & Storage: Supabase (Postgres + Storage + Auth with Google OAuth)
AI: Anthropic Claude API for vision-based photo analysis and post generation
Social posting: Bluesky AT Protocol SDK, Facebook Graph API v25.0, Instagram Graph API
Automation: Self-hosted n8n on Railway for scheduled multi-tenant posting every 30 minutes

The posting pipeline flows through a single postPetToBluesky() helper. It:

  1. Authenticates with Bluesky
  2. Uploads the image blob
  3. Creates the post with rich-text hashtag facets
  4. Marks the pet as posted in the database
  5. Returns the API response immediately

After that, it triggers Facebook and Instagram posting in the background using a non-blocking Promise.all().


Challenges I ran into

Instagram's 2-step posting flow

Unlike Bluesky and Facebook, Instagram requires creating a media container first, then polling until it's ready before publishing. I implemented a status-polling loop (checking every 2 seconds, up to 60 seconds) to handle variable processing times.

Facebook token management

Short-lived User tokens from the Graph API Explorer kept expiring. I switched to a System User token via Meta Business Manager, which does not expire.

Non-blocking social posting

Early versions made users wait 10+ seconds for Instagram to finish before the UI responded. I fixed this by returning the response immediately after Bluesky posts, then firing Facebook and Instagram in the background.

Multi-tenant scheduling

Building an n8n workflow that dynamically serves all organizations at their own posting intervals required a custom /due-for-posting endpoint that calculates which organizations are ready based on their last post time.


Accomplishments that I am proud of

A shelter staff member can go from a raw photo to a live post on three platforms in under 30 seconds.

The AI photo analysis is genuinely impressive — it correctly identifies breeds, estimates ages, and picks up on personality from body language and posture.

I am especially proud that the hardest part of the UX — writing a good caption — is the part the user never has to do.

I am also proud of the architecture:

  • Every shelter is a fully isolated tenant
  • A single n8n workflow serves all tenants
  • The entire posting pipeline (Bluesky, Facebook, Instagram) is handled by one backend function

What I learned

  • Claude's vision capabilities go far beyond simple image description — with the right prompt, it can extract structured, form-ready data from a photo
  • Instagram's Graph API has significantly more friction than Bluesky or Facebook — the container polling pattern is a non-obvious requirement
  • Non-blocking architecture matters for UX — users don't need to wait for every downstream side effect to complete
  • Meta's System User tokens (via business.facebook.com) are the right long-term solution for stable, non-expiring API access

What's next for PawPost

  • Engagement analytics — track likes, reposts, and replies per pet across platforms to identify what drives the most interest
  • Automatically import pet profiles from existing shelter databases so staff never have to enter data twice
Share this project:

Updates