Unified Social Post Publishing Workflow - Complete Documentation
๐ Table of Contentsโ
- Overview
- Architecture
- Workflow Steps
- API Endpoints
- Usage Examples
- Testing
- Deployment
- Troubleshooting
๐ฏ Overviewโ
The Unified Social Post Publishing Workflow is a complete refactoring of the social media publishing system, reducing complexity from 351 lines to 67 lines (81% reduction) while improving maintainability, testability, and security.
Key Improvementsโ
Before (Monolithic Route Handler):
- โ 351 lines of business logic in route handler
- โ Difficult to test individual components
- โ Hard to modify or extend
- โ Validation scattered throughout code
- โ No clear separation of concerns
After (Modular Workflow):
- โ 67-line route handler (thin HTTP wrapper)
- โ 11 independently testable workflow steps
- โ Clear separation of concerns
- โ Easy to modify and extend
- โ Centralized validation
- โ Secure token management
๐๏ธ Architectureโ
Workflow Flowโ
POST /admin/social-posts/:id/publish
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Route Handler (67 lines) โ
โ - Validates request โ
โ - Calls unified workflow โ
โ - Returns response โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Unified Workflow (11 Steps) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 1. Load Post with Platform โ
โ 2. Validate Platform โ
โ 3. Decrypt Credentials โ
โ 4. Detect Smart Retry โ
โ 5. Extract Target Accounts โ
โ 6. Extract Content โ
โ 7. Determine Content Type โ
โ 8. Validate Content Compatibility โ
โ 9. Route to Platform Workflow โ
โ 10. Merge Publish Results โ
โ 11. Update Post with Results โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Platform-Specific Workflows โ
โ - Twitter Workflow โ
โ - Facebook/Instagram Workflow โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Directory Structureโ
src/
โโโ api/admin/social-posts/[id]/publish/
โ โโโ route.ts # 67-line route handler
โ โโโ validators.ts # Request validation
โ
โโโ workflows/socials/
โ โโโ publish-social-post-unified.ts # Main workflow
โ โ
โ โโโ steps/
โ โโโ index.ts # Export all steps
โ โโโ load-post-with-platform.ts # Step 1
โ โโโ validate-platform.ts # Step 2
โ โโโ decrypt-credentials.ts # Step 3
โ โโโ detect-smart-retry.ts # Step 4
โ โโโ extract-target-accounts.ts # Step 5
โ โโโ extract-content.ts # Step 6
โ โโโ determine-content-type.ts # Step 7
โ โโโ validate-content-compatibility.ts # Step 8
โ โโโ route-to-platform-workflow.ts # Step 9
โ โโโ merge-publish-results.ts # Step 10
โ โโโ update-post-with-results.ts # Step 11
โ
โโโ modules/socials/utils/
โโโ token-helpers.ts # Token encryption/decryption
๐ Workflow Stepsโ
Step 1: Load Post with Platformโ
File: load-post-with-platform.ts
Purpose: Loads the social post by ID with its associated platform.
Input:
{ post_id: string }
Output:
{
post: SocialPost,
platform: SocialPlatform
}
Validation:
- Post exists
- Platform is associated with post
Step 2: Validate Platformโ
File: validate-platform.ts
Purpose: Validates platform configuration and status.
Input:
{ platform: SocialPlatform }
Output:
{
platform_name: string,
platform_category: string
}
Validation:
- Platform is active
- Platform has required configuration
Step 3: Decrypt Credentialsโ
File: decrypt-credentials.ts
Purpose: Securely decrypts OAuth tokens from encrypted storage.
Input:
{
platform: SocialPlatform,
platform_name: string
}
Output:
{
decrypted_token: string,
api_config: Record<string, unknown>
}
Security:
- Uses AES-256-GCM encryption
- Tokens never logged in plaintext
- Supports key rotation
Step 4: Detect Smart Retryโ
File: detect-smart-retry.ts
Purpose: Implements smart retry logic - only retry failed platforms.
Input:
{
post: SocialPost,
platform_name: string
}
Output:
{
is_retry: boolean,
target_platforms: string[],
previous_results: PublishResult[]
}
Logic:
- Checks
post.insights.publish_results - For FBINSTA: Only retry failed platform (Facebook OR Instagram)
- For single platforms: Retry if failed
Step 5: Extract Target Accountsโ
File: extract-target-accounts.ts
Purpose: Extracts account IDs from post metadata or overrides.
Input:
{
post: SocialPost,
platform_name: string,
override_page_id?: string,
override_ig_user_id?: string
}
Output:
{
page_id?: string,
ig_user_id?: string
}
Validation:
- Facebook requires
page_id - Instagram requires
ig_user_id - FBINSTA requires both
Step 6: Extract Contentโ
File: extract-content.ts
Purpose: Extracts caption and media from post.
Input:
{ post: SocialPost }
Output:
{
caption: string,
media_attachments: Record<string, MediaAttachment>
}
Step 7: Determine Content Typeโ
File: determine-content-type.ts
Purpose: Analyzes media and determines content type.
Input:
{ media_attachments: Record<string, MediaAttachment> }
Output:
{
content_type: "photo" | "video" | "carousel" | "text"
}
Logic:
- No media โ
text - 1 image โ
photo - 1 video โ
video - Multiple images โ
carousel
Step 8: Validate Content Compatibilityโ
File: validate-content-compatibility.ts
Purpose: Validates content against platform-specific rules.
Input:
{
platform_name: string,
content_type: string,
caption: string,
media_count: number
}
Validation Rules:
Instagram:
- โ Text-only posts not supported
- โ Photo, video, carousel supported
Twitter:
- โ Max 280 characters
- โ Max 4 images
- โ Text-only supported
Facebook:
- โ All content types supported
Step 9: Route to Platform Workflowโ
File: route-to-platform-workflow.ts
Purpose: Routes to appropriate platform-specific workflow.
Input:
{
platform_name: string,
post: SocialPost,
// ... all previous step outputs
}
Routing Logic:
twitterโpublishSocialPostWorkflow(Twitter)facebook,instagram,fbinstaโpublishToBothPlatformsUnifiedWorkflow
Output:
{
facebook?: PublishResult,
instagram?: PublishResult,
twitter?: PublishResult
}
Step 10: Merge Publish Resultsโ
File: merge-publish-results.ts
Purpose: Merges new results with previous attempts (for smart retry).
Input:
{
new_results: PublishResults,
previous_results: PublishResult[],
is_retry: boolean
}
Output:
{
merged_results: PublishResult[]
}
Logic:
- First attempt: Use new results
- Retry: Merge with previous, keeping successful results
Step 11: Update Post with Resultsโ
File: update-post-with-results.ts
Purpose: Updates post with publish results and status.
Input:
{
post: SocialPost,
merged_results: PublishResult[],
is_retry: boolean,
platform_name: string
}
Updates:
status:publishedorfailedposted_at: Current timestamp (if successful)insights.publish_results: Merged resultsinsights.facebook_post_id: Facebook post IDinsights.instagram_media_id: Instagram media IDinsights.twitter_tweet_id: Twitter tweet IDerror_message: Error details (if failed)
Output:
{
success: boolean,
updated_post: SocialPost,
results: PublishResults,
retry_info?: RetryInfo
}
๐ API Endpointsโ
POST /admin/social-posts/:id/publishโ
Publishes a social media post to configured platforms.
Request:
POST /admin/social-posts/post_123/publish
Content-Type: application/json
Authorization: Bearer <admin_token>
{
"override_page_id": "987654321", // Optional
"override_ig_user_id": "123456789" // Optional
}
Response (Success):
{
"success": true,
"post": {
"id": "post_123",
"status": "published",
"posted_at": "2025-11-19T14:00:00Z",
// ... other post fields
},
"results": {
"facebook": {
"success": true,
"post_id": "fb_post_123",
"url": "https://facebook.com/..."
},
"instagram": {
"success": true,
"media_id": "ig_media_456",
"url": "https://instagram.com/..."
}
},
"retry_info": {
"is_retry": false,
"retried_platforms": []
}
}
Response (Failure):
{
"success": false,
"post": {
"id": "post_123",
"status": "failed",
"error_message": "Publishing failed: Invalid OAuth access token"
},
"results": {
"facebook": {
"success": false,
"error": "Invalid OAuth access token"
}
}
}
Error Codes:
400- Validation error (missing page_id, invalid content, etc.)404- Post not found500- Server error
๐ก Usage Examplesโ
Example 1: Publish to Facebookโ
// Create a post
const post = await api.post("/admin/social-posts", {
name: "My Facebook Post",
caption: "Hello Facebook! #test",
status: "draft",
platform_id: "facebook_platform_id",
media_attachments: {
"0": {
type: "image",
url: "https://example.com/image.jpg"
}
},
metadata: {
page_id: "123456789",
publish_target: "facebook"
}
})
// Publish it
const result = await api.post(`/admin/social-posts/${post.id}/publish`, {})
Example 2: Publish to Both Facebook & Instagramโ
const post = await api.post("/admin/social-posts", {
name: "My FBINSTA Post",
caption: "Hello both platforms!",
status: "draft",
platform_id: "fbinsta_platform_id",
media_attachments: {
"0": {
type: "image",
url: "https://example.com/image.jpg"
}
},
metadata: {
page_id: "123456789",
ig_user_id: "987654321",
publish_target: "both"
}
})
const result = await api.post(`/admin/social-posts/${post.id}/publish`, {})
Example 3: Smart Retry (Only Failed Platform)โ
// First attempt - Facebook succeeds, Instagram fails
const firstAttempt = await api.post(`/admin/social-posts/${post.id}/publish`, {})
// Result: { facebook: { success: true }, instagram: { success: false } }
// Retry - Only retries Instagram
const retryAttempt = await api.post(`/admin/social-posts/${post.id}/publish`, {})
// Result: { facebook: { success: true }, instagram: { success: true } }
// Facebook result preserved from first attempt!
Example 4: Override Account IDsโ
// Override page_id at publish time
const result = await api.post(`/admin/social-posts/${post.id}/publish`, {
override_page_id: "different_page_id",
override_ig_user_id: "different_ig_user_id"
})
๐งช Testingโ
Integration Testsโ
File: /integration-tests/http/socials/unified-publish-workflow.spec.ts
Test Coverage:
- โ Workflow execution with fake tokens (expects Facebook API error)
- โ
Validation of missing
page_id - โ
Rejection of array format for
media_attachments
Run Tests:
yarn test:integration:http ./integration-tests/http/socials/unified-publish-workflow.spec.ts
Test Strategyโ
What We Test:
- โ Workflow structure and step execution
- โ Validation logic (page_id, content compatibility, etc.)
- โ Error handling and messaging
- โ Data format validation
What We Don't Test (requires real OAuth tokens):
- โ Actual publishing to Facebook/Instagram/Twitter
- โ Real API responses
- โ Live token validation
For E2E Testing (manual/staging):
- Set up test social media accounts
- Use real OAuth tokens
- Verify posts actually appear on platforms
๐ Deploymentโ
Prerequisitesโ
-
Encryption Keys (if using token encryption)
# Generate encryption key
openssl rand -hex 32
# Add to environment
ENCRYPTION_KEY=your_generated_key -
Database Migration (if schema changed)
yarn medusa migrations run
Deployment Stepsโ
-
Staging Deployment
# Deploy to staging
git push staging main
# Monitor logs
tail -f /var/log/medusa/staging.log
# Test publishing
curl -X POST https://staging.example.com/admin/social-posts/test_id/publish \
-H "Authorization: Bearer $STAGING_TOKEN" -
Production Deployment
# Deploy to production
git push production main
# Monitor metrics
# - Response times
# - Error rates
# - Success rates -
Rollback Plan
# If issues occur, rollback
git revert HEAD
git push production main
๐ง Troubleshootingโ
Common Issuesโ
1. "Invalid OAuth access token"โ
Cause: Token is invalid or expired
Solution: Re-authenticate the platform
2. "No Facebook page_id found"โ
Cause: Missing page_id in post metadata
Solution: Add page_id to metadata or provide override_page_id
3. "Text-only posts are not supported on Instagram"โ
Cause: Instagram requires media
Solution: Add at least one image or video
4. "Tweet exceeds 280 characters"โ
Cause: Caption too long for Twitter
Solution: Shorten caption to 280 characters or less
5. "Expected type: 'object' for field 'media_attachments', got: 'array'"โ
Cause: Using array format instead of object
Solution: Change media_attachments from [] to {}
// โ Wrong
media_attachments: [
{ type: "image", url: "..." }
]
// โ
Correct
media_attachments: {
"0": { type: "image", url: "..." }
}
Debug Modeโ
Enable detailed logging:
// Each workflow step logs its progress
// Check console for step-by-step execution:
// [Load Post] โ Loaded post post_123 with platform Facebook
// [Validate Platform] โ Platform Facebook is active
// [Decrypt Credentials] โ Access token decrypted successfully
// ...
๐ Metrics & Monitoringโ
Key Metrics to Trackโ
- Success Rate: % of successful publishes
- Error Rate: % of failed publishes
- Response Time: Average time to publish
- Retry Rate: % of posts that needed retry
- Platform-Specific Success: Success rate per platform
Monitoring Setupโ
// Example monitoring with custom metrics
const publishMetrics = {
total_attempts: 0,
successful: 0,
failed: 0,
retries: 0,
avg_response_time: 0
}
// Track in workflow
publishMetrics.total_attempts++
if (result.success) publishMetrics.successful++
else publishMetrics.failed++
๐ Best Practicesโ
-
Always Use Object Format for media_attachments
media_attachments: { "0": {...}, "1": {...} } -
Provide Required Account IDs
- Facebook:
page_id - Instagram:
ig_user_id - FBINSTA: Both
- Facebook:
-
Handle Errors Gracefully
- Check
result.successbefore assuming success - Display error messages to users
- Implement retry logic for transient failures
- Check
-
Test Before Production
- Use staging environment
- Test all platforms
- Verify content appears correctly
-
Monitor Production
- Track success/failure rates
- Set up alerts for high error rates
- Monitor API rate limits
๐ Additional Resourcesโ
๐ค Supportโ
For issues or questions:
- Check Troubleshooting section
- Review integration tests for examples
- Check workflow step logs for detailed execution flow
- Contact development team
Last Updated: November 19, 2025
Version: 1.0.0
Status: โ
Production Ready