yeetpostyeetpost

Security checklist

Ship your integration without leaking a key.


This checklist helps you (or your AI coding assistant) verify that your yeetpost integration follows security best practices.

Using AI? Copy this page into your AI prompt when implementing yeetpost.


#Note about different types of content

yeetpost posts to public social media channels, and will support private messaging channels once they ship:

  • Posting connections (LinkedIn, X, Bluesky, Discord, Telegram, Mastodon) → High risk. A LinkedIn, X, Bluesky or Mastodon post is public and permanent, and a Discord or Telegram post lands in a channel people are reading. These are live today.
  • Private channels (Slack, SMS, email) → Lower risk and often higher volume. Used for notifications to yourself or your team. These are coming soon.

You should consider which platforms you use and the risks associated with each.

We've included nuanced guidance for each type of content in the steps below.


#1. Keep your API key secret (CRITICAL)

Your yeetpost API key grants full access to post on your connected accounts. Treat it like a password.

For posting connections (the six live platforms):

  • Store the API key in a server-side environment variable (e.g., YEETPOST_API_KEY)
  • In Next.js, do NOT prefix with NEXT_PUBLIC_, this exposes it to the browser
  • Never import or reference the API key in any client-side code
  • Never include it in API responses sent to the frontend
  • If leaked: immediately delete the key in the yeetpost dashboard and create a new one

For private channels (Slack, SMS, email), coming soon:

  • Same rules apply. A leaked key could spam your Slack or send emails on your behalf.

#2. No frontend-callable posting endpoints (CRITICAL)

Your app should never have a generic “post to yeetpost” API endpoint that the frontend calls directly.

For posting connections (the six live platforms):

  • ❌ Bad: POST /api/post-to-yeetpost?message=... called from the browser with message body (allows users to post anything)
  • ❌ Bad: POST /api/post-approved-project-to-yeetpost?projectId=... called from the browser (message not user-defined, but can still be spammed or abused)
  • ✅ Good: The code that calls yeetpost lives inside your existing backend logic (e.g., inside an authenticated endpoint like /api/approve-project which contains all logic related to a business function and calls yeetpost at the end)
  • ✅ Good: Trigger posts from backend events (database triggers, webhooks, cron jobs)

For private channels (Slack, SMS, email), coming soon:

  • Same principle, though consequences are less severe
  • Still avoid exposing a generic endpoint that could be abused to spam your channels

#3. Use hardcoded templates, not user input (CRITICAL)

Never let users control what gets posted. The message content should be defined in your code.

For posting connections (the six live platforms):

  • ❌ Bad: postToLinkedIn(req.body.message): anyone can post anything
  • ❌ Bad: postToLinkedIn(project.description): if description isn’t moderated, bad actors can inject content
  • ✅ Good: if (project.approved_by_admin) postToLinkedIn("New project published: " + project.name): template is hardcoded
  • The only dynamic parts should be identifiers that you control (IDs, names from your database)

For private channels (Slack, SMS, email), coming soon:

  • More flexibility is acceptable here since these are private notifications (e.g., notifications to yourself or your team)
  • You might want to include user-submitted data for moderation purposes (e.g., “New signup: [email protected]”)
  • Still avoid passing raw user input without any structure

#4. Moderate dynamic content (CRITICAL)

If your template includes any user-generated content, that content must be reviewed before posting.

For posting connections (the six live platforms):

  • Any user-provided string (username, project name, bio) could contain:
    • Malware links
    • Offensive content
    • Spam or scams
    • Impersonation attempts
  • Options for moderation:
    • Admin approval queue before posting
    • Only post content that’s already been publicly visible (e.g., top leaderboard entries)
    • Character allowlists (alphanumeric only)
    • Link validation (only allow your own domains)
  • ❌ Bad: Post immediately when a new user registers (username could be anything)
  • ✅ Good: Post weekly highlights that an admin has already reviewed

For private channels (Slack, SMS, email), coming soon:

  • Less critical since you’re notifying yourself/your team
  • In fact, you might use these notifications for moderation (“New user registered: [suspicious_name]”)
  • Still consider: could someone abuse this to spam your Slack with 10,000 messages?

Prevent bugs or abuse from flooding your social accounts.

For posting connections (the six live platforms):

  • Track what’s been posted in your database (e.g., posted_to_linkedin_at column)
  • Check before posting: “Has this already been posted?”
  • Add global rate limits: max N posts per hour/day
  • Consider a cooldown period between posts
  • Consequences of bugs:
    • Account banned by platform
    • Followers unfollow due to spam
    • Professional reputation damage

For private channels (Slack, SMS, email), coming soon:

  • Still important, nobody wants 1,000 Slack notifications
  • But easier to recover from: just mute the channel temporarily

Keep a record of what was posted, when, and why.

For posting connections (the six live platforms):

  • At minimum: log every post with timestamp and content to your application logs
  • Better: store in database with trigger source (which event caused this post?)
  • Best: implement audit logging with user attribution if applicable
  • Helps with: debugging, abuse detection, compliance

For private channels (Slack, SMS, email), coming soon:

  • Same recommendations, though audit trail is less critical
  • Useful for debugging notification issues

Platforms have their own limits, but you should set stricter ones based on your needs.

For posting connections (the six live platforms):

  • Platforms allow 100s, 1000s or even 10,000s of characters
  • Your posts are probably much shorter, so set limits in your code accordingly to avoid surprises
  • Prevents edge cases where a bug generates unexpectedly long content
  • Example: if (message.length > 500) message = message.slice(0, 500) + "..."

For private channels (Slack, SMS, email), coming soon:

  • Consider on a case-by-case basis

#What to do if you suspect your API key has been compromised

In the case of any of the following:

  • You suspect your API key has been compromised
  • You noticed your API key has momentarily been exposed to the frontend
  • Your API key was used to post something you do not recognize

Follow these steps immediately:

  • Log in to yeetpost dashboard
  • Delete the compromised API key
    • If you are not sure which key is compromised, delete all keys
  • Investigate how the key was compromised
  • After you have made sure the security issue has been resolved, create a new API key