TL;DR
- This is the implementation guide, not the sales pitch. For “why API-first,” read our white label management guide.
- bundle.social’s multi-tenant model: Organization → Teams → Social Accounts. Your “client” maps to our “team.”
- API keys are scoped at the organization level. One key, all your clients’ data.
- Two-layer rate limiting: our API limits + per-platform daily posting limits. Handle both.
- Webhooks fire at the organization level -
post.published,post.failed,social-account.created, and more. - Full end-to-end flow from user signup to first published post, with production-ready code.
Who This Is For
You’ve already decided to build a social media SaaS (or white-label tool) on top of an API. You don’t need another comparison table - you need architecture diagrams, data model mappings, and code that works in production. If you’re still weighing your options, start here:- White Label Social Media Management - why API-first beats traditional dashboards
- White Label Social Media Posting - the complete guide for agencies
The Data Model
Here’s how bundle.social organizes multi-tenant data:| Your concept | bundle.social concept |
|---|---|
| Your SaaS account | Organization |
| Your client / workspace | Team |
| Client’s social profiles | Social Accounts (within a Team) |
| Your API credentials | API Key (org-scoped) |
| Your webhook endpoints | Webhooks (org-scoped) |
One Organization, many Teams. You don’t create separate bundle.social accounts for each client. Create one Organization, get your API key, and spin up Teams programmatically. All billing flows through your single subscription.
Mapping Your Users to Our Organizations
Here’s where architecture meets your database. You need to track which of YOUR users maps to which of OUR teams. Minimal schema (use whatever DB you like):API Key Architecture
API keys are scoped to your Organization - not to individual teams. One key gives you access to all teams under your org.How It Works
- Create API keys in the dashboard (up to 50 per org)
- Every public API request uses the
x-api-keyheader - We identify your organization from the key and scope all data accordingly
Key Management
- Rotate regularly. Use the roll endpoint to regenerate a key without downtime
- Don’t share keys across environments. Separate keys for dev, staging, prod
- Store in env variables. We hash keys with SHA-256 on our end, so even we can’t read them after creation
The Complete Flow: Signup to First Post
Here’s the full end-to-end walkthrough. New user signs up for your platform, connects their socials, and publishes a post. Every step maps to a real API call.Step 1: User Signs Up → Create a Team
Step 2: Connect Social Accounts → Portal Link
Instead of building OAuth flows for 14+ platforms yourself (please don’t), use our hosted connection portal:Want full control? You can build custom OAuth flows using
POST /api/v1/social-account/connect to get OAuth URLs per platform. But tbh, the hosted portal saves weeks of work and handles edge cases you haven’t thought of yet. See the Connect Social Accounts docs for both approaches.Step 3: Upload Media
Step 4: Create the Post
data. Instagram needs a type (POST, REEL, STORY), TikTok needs privacy, YouTube needs madeForKids, Pinterest needs a boardId, and so on. Platform-specific fields are all documented in our Platform Guides.
Webhook Integration
Webhooks fire at the organization level. All events from all teams hit the same webhook endpoints. You get up to 5 webhook URLs per organization.Available Events
| Event | When it fires | Why you care |
|---|---|---|
post.published | Post went live on the platform | Update your UI, notify the user |
post.failed | Post failed after retries | Alert the user, log the error |
comment.published | First comment was posted | Track auto-comment status |
social-account.created | New social account connected | Update available platforms in your UI |
social-account.deleted | Account disconnected | Remove from your UI, alert the user |
team.created | New team created | Sync with your workspace list |
team.updated | Team details changed | Also fires when social accounts are added/removed |
team.deleted | Team deleted | Clean up your database |
Handling Webhooks
Delivery & Reliability
| Setting | Value |
|---|---|
| Timeout | 15 seconds per delivery |
| Max attempts | 3 (initial + 2 retries) |
| Backoff | Exponential, starting at 30s |
| Auto-disable | After 50 consecutive failures in 24h |
Two-Layer Rate Limiting
This trips up most developers. There are TWO separate rate limiting systems, and you need to handle both.Layer 1: API Rate Limits (Our Infrastructure)
These protect the API from getting hammered:| Layer | Window | Max Requests |
|---|---|---|
| Burst | 1 second | 100 |
| Short | 10 seconds | 500 |
| Minute | 1 minute | 2,000 |
429 Too Many Requests. Back off exponentially.
Layer 2: Platform Posting Limits (Per Social Account, Per Day)
Daily caps on how many posts each connected account can make, varying by subscription tier:| Platform | FREE | PRO | BUSINESS |
|---|---|---|---|
| 10/day | 20/day | 25/day | |
| TikTok | 5/day | 10/day | 15/day |
| Twitter/X | 5/day | 15/day | 15/day |
| 10/day | 18/day | 24/day | |
| YouTube | 10/day | 10/day | 15/day |
| 10/day | 24/day | 36/day |
Building Your Own Limit Layer
Here’s the thing - you probably want YOUR OWN limits on top of ours. If your “Starter” plan allows 100 posts/month but your bundle.social plan allows 1,000, that’s your problem to enforce.Keep it simple. A counter in your database, reset monthly, is all you need. Don’t over-engineer this. Check your limits before hitting our API, and handle our error responses for the rest.
The OAuth Problem (Solved)
Connecting social accounts is the hardest part of any social media integration. Each platform has different OAuth flows, token formats, scopes, and refresh cycles. We’ve been dealing with this in production across 14+ platforms, so you don’t have to.Hosted Portal (Recommended)
Thecreate-portal-link endpoint generates a branded page where users connect accounts:
- Custom logo (
logoUrl) - Hidden “Powered by” (
hidePoweredBy: true) - Custom back button text (
goBackButtonText) - 13 languages (en, pl, fr, de, es, it, nl, pt, ru, tr, zh, hi, sv)
- Platform filtering - only show the platforms you want per session
- Connection limit - cap how many accounts a user can connect (
maxSocialAccountsConnected)
Channel Selection
For YouTube, Facebook, Instagram, LinkedIn, and Google Business, there’s an extra step after OAuth - the user needs to pick which page/channel/location to use. The hosted portal handles this automatically. If you’re building a custom OAuth flow, you’ll need to callPOST /api/v1/social-account/set-channel yourself.
See the Connect Social Accounts docs for both flows in detail.
Production Checklist
Before you ship, run through this. Trust me.Security
- API key in environment variables, not hardcoded
- Webhook signatures verified on every delivery
- HTTPS on your webhook endpoint
- No API keys in client-side code (all calls from your backend)
Data Integrity
- Team IDs mapped correctly in your database
- Error handling for failed API calls (especially post creation)
- Idempotent webhook handlers (we may deliver events more than once)
Limits & Monitoring
- Your own rate limiting layer on top of ours
- Monthly usage tracking surfaced to your users
- Alerting when approaching plan limits (80% threshold)
User Experience
- Graceful error messages when posts fail (platform-specific errors from
post.failedwebhook) - Social account reconnection flow (tokens expire, especially Instagram and TikTok)
- Clear feedback on which platforms are connected per workspace
Webhooks
-
post.publishedandpost.failedhandled at minimum - Fast response times (< 15s, async processing for anything heavy)
- Monitoring for consecutive failures (we auto-disable at 50)
Next Steps
Architecture done. Now go ship it.API Documentation
Full endpoint reference and request/response schemas
SDK for TypeScript
Typed SDK with auth headers and webhook verification built in
Platform Guides
Platform-specific requirements, limits, and quirks for all 14+ platforms
Code Examples
Copy-paste implementations for common integration patterns
Already building? Running into edge cases? Check the API docs or reach out - we’ve been running this in production long enough to know where the gotchas hide.