Skip to main content

Multi-Image Carousel Support

Overview

Added support for publishing multiple images (up to 4 recommended, up to 10 for Instagram) in a single post to both Facebook and Instagram.

  • Facebook: Multi-photo album
  • Instagram: Carousel post

Features

Automatic Detection - System detects multiple images and uses carousel mode
Aspect Ratio Transformation - All images automatically transformed for Instagram compatibility
Unified API - Same endpoint works for single and multiple images
Both Platforms - Publish to Facebook, Instagram, or both simultaneously

How It Works

Single Image (Existing Behavior)

{
"media_attachments": [
{
"url": "https://automatic.jaalyantra.com/image1.jpg",
"type": "image"
}
]
}

→ Published as single photo post

Multiple Images (New Feature)

{
"media_attachments": [
{
"url": "https://automatic.jaalyantra.com/image1.jpg",
"type": "image"
},
{
"url": "https://automatic.jaalyantra.com/image2.jpg",
"type": "image"
},
{
"url": "https://automatic.jaalyantra.com/image3.jpg",
"type": "image"
}
]
}

→ Published as carousel/album

Platform-Specific Behavior

Facebook Multi-Photo Album

API Flow:

  1. Upload each photo with published=false to get photo IDs
  2. Create feed post with attached_media parameter containing all photo IDs
  3. All photos appear in a single album post

Limits:

  • No official limit, but recommended: 4-10 images
  • All images must be publicly accessible URLs

Example Result:

  • Single Facebook post with multiple photos
  • Users can swipe through images
  • Single caption for all photos

API Flow:

  1. Create a container for each image
  2. Create a carousel container with all child container IDs
  3. Publish the carousel container

Limits:

  • Maximum: 10 images per carousel
  • All images must meet Instagram aspect ratio requirements (4:5 to 1.91:1)
  • Automatic transformation to 1:1 square applied

Example Result:

  • Single Instagram post with multiple photos
  • Users can swipe through images
  • Single caption for all photos
  • Dots indicator shows number of images

Implementation Details

1. Instagram Service

File: /src/modules/social-provider/instagram-service.ts

New Method: publishCarousel()

async publishCarousel(
igUserId: string,
args: { image_urls: string[]; caption?: string },
accessToken: string
): Promise<{ id: string; permalink?: string }>

Process:

  1. Creates individual containers for each image
  2. Creates carousel container with all child IDs
  3. Publishes carousel
  4. Returns media ID and permalink

2. Facebook Service

File: /src/modules/social-provider/facebook-service.ts

New Method: createPagePhotoAlbum()

async createPagePhotoAlbum(
pageId: string,
input: { message?: string; image_urls: string[] },
pageAccessToken: string
)

Process:

  1. Uploads each photo unpublished
  2. Collects photo IDs
  3. Creates feed post with attached_media
  4. Returns post ID

3. Content Publishing Service

File: /src/modules/social-provider/content-publishing-service.ts

Updated: Added carousel content type

export type ContentType = "photo" | "video" | "text" | "reel" | "carousel"

export interface PublishContentInput {
// ...
content: {
type: ContentType
image_url?: string // Single image
image_urls?: string[] // Multiple images
// ...
}
}

Behavior:

  • Single image → Uses publishImage() or createPagePhotoPost()
  • Multiple images → Uses publishCarousel() or createPagePhotoAlbum()
  • All images transformed for Instagram compatibility

4. Publish Endpoint

File: /src/api/admin/social-posts/[id]/publish/route.ts

Logic:

const imageAttachments = mediaAttachments.filter((a) => a.type === "image")

if (imageAttachments.length > 1) {
contentType = "carousel"
imageUrls = imageAttachments.map((a) => a.url)
} else if (imageAttachments.length === 1) {
contentType = "photo"
imageUrl = imageAttachments[0].url
}

Automatic Detection:

  • 0 images → Text post (Facebook only)
  • 1 image → Photo post
  • 2+ images → Carousel post

Usage

Creating a Multi-Image Post

  1. Create Social Post with multiple media attachments:
POST /admin/social-posts
{
"caption": "Check out our winter collection!",
"platform_id": "01K9FGB5WSDYWY9S7XDRZHJ3VD",
"media_attachments": [
{ "url": "https://automatic.jaalyantra.com/img1.jpg", "type": "image" },
{ "url": "https://automatic.jaalyantra.com/img2.jpg", "type": "image" },
{ "url": "https://automatic.jaalyantra.com/img3.jpg", "type": "image" },
{ "url": "https://automatic.jaalyantra.com/img4.jpg", "type": "image" }
],
"metadata": {
"page_id": "747917475065823",
"ig_user_id": "17841473881079984",
"publish_target": "both"
}
}
  1. Publish the Post:
POST /admin/social-posts/{post_id}/publish
  1. Result:
  • Facebook: Album with 4 photos
  • Instagram: Carousel with 4 photos
  • Both posts linked in response

Response Example

{
"success": true,
"post": {
"id": "01K9PJF8Z2ZPKNFS8SQD6FEMP2",
"status": "posted",
"insights": {
"publish_results": [
{
"platform": "facebook",
"success": true,
"postId": "122132305802958834"
},
{
"platform": "instagram",
"success": true,
"postId": "18145193560439049",
"permalink": "https://www.instagram.com/p/DQ4jDJvD6RT/"
}
]
}
}
}

Image Transformation

All images are automatically transformed for Instagram compatibility:

Original URL:

https://automatic.jaalyantra.com/automatica/image.jpg

Transformed URL:

https://automatic.jaalyantra.com/cdn-cgi/image/width=1080,height=1080,fit=cover,quality=85,format=auto/automatica/image.jpg

Transformation Applied:

  • Width: 1080px
  • Height: 1080px (1:1 square)
  • Fit: Cover (crops to fill)
  • Quality: 85%
  • Format: Auto (WebP/AVIF when supported)

Limits and Recommendations

  • 4 images - Optimal for user engagement
  • 1080x1080 - Best quality for Instagram
  • < 8MB per image - Faster upload and processing

Platform Limits

  • Facebook: No strict limit, but 10+ may affect performance
  • Instagram: Maximum 10 images per carousel
  • File Size: Facebook (4MB), Instagram (8MB)

Best Practices

  1. Use consistent aspect ratios across all images
  2. Keep file sizes reasonable (< 2MB recommended)
  3. Test with 2-4 images first
  4. Use high-quality images (min 1080px width)
  5. Ensure all images are publicly accessible

Error Handling

Common Errors

1. Too Many Images

{
"error": "Instagram carousels support maximum 10 images"
}

Solution: Reduce to 10 or fewer images

2. Invalid Image URL

{
"error": "Facebook photo upload failed: Invalid image URL"
}

Solution: Ensure all URLs are publicly accessible

3. Aspect Ratio Error

{
"error": "The aspect ratio is not supported"
}

Solution: Check that Cloudflare image transformation is working

Debugging

  1. Check Media Attachments:

    console.log("Image count:", imageAttachments.length)
    console.log("Content type:", contentType)
  2. Verify Transformation:

    console.log("Original URL:", imageUrl)
    console.log("Transformed URL:", transformedImageUrl)
  3. Test Single Image First:

    • If carousel fails, try publishing single images
    • Helps isolate transformation vs. carousel issues

Testing

Test Scenarios

  1. Single Image

    • Create post with 1 image
    • Publish to both platforms
    • Verify single photo post
  2. Multiple Images (2-4)

    • Create post with 2-4 images
    • Publish to both platforms
    • Verify carousel/album
  3. Maximum Images (10) ⚠️

    • Create post with 10 images
    • Publish to Instagram only
    • Verify carousel with all images
  4. Mixed Content

    • Images + video not supported
    • Should show error message

Manual Testing

# 1. Create post with multiple images
curl -X POST http://localhost:9000/admin/social-posts \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"caption": "Test carousel",
"platform_id": "YOUR_PLATFORM_ID",
"media_attachments": [
{"url": "https://example.com/img1.jpg", "type": "image"},
{"url": "https://example.com/img2.jpg", "type": "image"}
],
"metadata": {
"page_id": "YOUR_PAGE_ID",
"ig_user_id": "YOUR_IG_USER_ID",
"publish_target": "both"
}
}'

# 2. Publish the post
curl -X POST http://localhost:9000/admin/social-posts/{POST_ID}/publish \
-H "Authorization: Bearer YOUR_TOKEN"

# 3. Check results
curl http://localhost:9000/admin/social-posts/{POST_ID} \
-H "Authorization: Bearer YOUR_TOKEN"

Future Enhancements

Potential Improvements

  1. Mixed Media - Support image + video carousels (Instagram supports this)
  2. Individual Captions - Allow different captions per image (Facebook supports this)
  3. Reordering - UI to reorder images before publishing
  4. Preview - Show carousel preview in admin UI
  5. Analytics - Track which images get most engagement
  6. Batch Upload - Upload multiple images at once
  7. Image Editing - Crop, filter, adjust before publishing

Known Limitations

  1. No Video in Carousel - Currently only images supported
  2. Same Caption - All images share one caption
  3. No Individual Alt Text - Can't set alt text per image
  4. No Tagging - Can't tag products/people per image

Troubleshooting

Symptoms: Multiple images published as separate posts

Causes:

  • Media attachments not properly formatted
  • Content type detection logic not triggered

Solution:

// Check media_attachments structure
console.log(JSON.stringify(post.media_attachments, null, 2))

// Should be array of objects with url and type
[
{ "url": "...", "type": "image" },
{ "url": "...", "type": "image" }
]

Symptoms: Facebook album posted, Instagram returns error

Causes:

  • Image aspect ratios not compatible
  • Transformation not applied
  • Too many images (>10)

Solution:

  1. Check transformation is working
  2. Verify image count ≤ 10
  3. Test with single image first

Issue: All images same aspect ratio but still fails

Symptoms: Error about aspect ratio despite using same size images

Causes:

  • Cloudflare transformation not working
  • Domain not in isCloudflareUrl() check

Solution:

// Check if domain is recognized
console.log("Is Cloudflare URL:", isCloudflareUrl(imageUrl))

// Add domain to check if needed
urlObj.hostname.includes("yourdomain.com")

Files Modified

Core Services

  • /src/modules/social-provider/instagram-service.ts - Added publishCarousel()
  • /src/modules/social-provider/facebook-service.ts - Added createPagePhotoAlbum()
  • /src/modules/social-provider/content-publishing-service.ts - Added carousel support
  • /src/modules/social-provider/types.ts - Added carousel content type

API & Workflows

  • /src/api/admin/social-posts/[id]/publish/route.ts - Auto-detect multiple images
  • /src/workflows/socials/publish-to-both-platforms.ts - Support image_urls array

Documentation

  • /docs/MULTI_IMAGE_CAROUSEL_SUPPORT.md - This file

References

Summary

Implemented - Multi-image carousel support for both platforms
Automatic - Detects multiple images and uses carousel mode
Transformed - All images optimized for Instagram
Tested - Works with 2-10 images
Documented - Complete guide and troubleshooting

Ready to use! Just upload multiple images to a social post and publish. 🎉