Implementation Blueprint

ROSME 2.0

Technical specification for Facebook auto-posting and ad campaign creation via n8n + Facebook Graph API + Marketing API

2
Core Workflows
FB Post + FB Ads
5
API Endpoints
Graph API v19.0+
~3hr
Build Time
With credentials ready
What Fasai Needs to Provide
Credentials and IDs that only the account owner can access. Everything else is pre-built.

Required Facebook Credentials

  • Facebook App ID
    From developers.facebook.com → Your App → Settings → Basic
  • Facebook App Secret
    Same page as App ID — click "Show" to reveal
  • Page Access Token (long-lived)
    Via System User in Business Manager, or exchange short-lived token
  • Facebook Page ID
    Page Settings → Page Transparency → Page ID, or from Graph API Explorer

Required Ads Manager Credentials

  • Ad Account ID (act_XXXXX)
    Business Manager → Business Settings → Ad Accounts → Copy ID (include act_ prefix)
  • Campaign ID
    Create a campaign shell in Ads Manager first → copy its ID. The workflow creates ad sets + ads under this campaign.
  • Facebook Pixel ID
    Events Manager → Data Sources → Pixel → Copy Pixel ID
  • System User Token
    Business Settings → System Users → Generate Token → Select app → Choose permissions → Set "Never" expiry

System User Token is the #1 priority. This is the permanent token that never expires. Without it, you'll need to refresh tokens every 60 days. To create one: Business Manager → Business Settings → System Users → Add → Admin role → Generate Token → Select your app → Enable: pages_manage_posts, pages_read_engagement, ads_management, ads_read → Set expiration to "Never" → Generate.

Required Permissions

PermissionForHow to Enable
pages_manage_postsPublishing posts to pageApp Review → Add Permission (or Live Mode for owned pages)
pages_read_engagementReading page data + getting page tokenApp Review → Add Permission
ads_managementCreating ad sets, creatives, adsMarketing API access (standard or advanced)
ads_readReading campaign performanceMarketing API access
business_managementManaging business assetsBusiness Manager system user
Workflow 1
Facebook Page Auto-Posting
Trigger: new row in Notion campaign DB → post photo + caption to Facebook Page via Graph API.

n8n Workflow Flow

1
Notion Trigger
New row in Posts DB
2
Validate
Check required fields
3
Get Token
Page Access Token
4
Post to FB
Graph API call
5
Update Notion
Mark as published
1

Notion Trigger — Poll Posts Database

Watches the "GTM Posts" Notion database for new entries with status = "Ready to Publish". Polls every 5 minutes.

// n8n Node: Notion Trigger (Polling) Type: n8n-nodes-base.notionTrigger Database: GTM Posts (created by this blueprint) Poll interval: Every 5 minutes Filter: Status = "Ready to Publish" Properties to fetch: Caption, Image URL, Page ID, Platform
2

IF Node — Validate Required Fields

Checks that Caption and Image URL both exist. Routes to error handler if missing.

// n8n Node: IF Condition: {{$json.Caption}} is not empty AND {{$json["Image URL"]}} is not empty True: → Continue to Post False: → Update Notion status to "Error: Missing Fields"
3

HTTP Request — Post Photo to Facebook

Uses the Graph API to post a photo with caption to the Facebook Page. This is the core API call.

// n8n Node: HTTP Request Method: POST URL: https://graph.facebook.com/v21.0/{PAGE_ID}/photos Query Parameters: url = {{$json["Image URL"]}} // Public image URL caption = {{$json.Caption}} // Post text/caption access_token = {{$credentials.fbPageToken}} // Page Access Token Response: { "id": "photo_id", "post_id": "page_post_id" } // For text-only posts (no image), use: URL: https://graph.facebook.com/v21.0/{PAGE_ID}/feed Body: { "message": "caption text", "access_token": "..." }
📸

Image URL must be publicly accessible. Facebook fetches the image from the URL. If using Notion file URLs, they expire after 1 hour. Solution: use a permanent host (Cloudflare R2, S3, Imgur) or fetch+re-upload within the workflow before posting.

4

Notion Update — Mark as Published

Updates the original Notion row: Status → "Published", adds the FB Post ID and publish timestamp.

// n8n Node: Notion (Update Page) Page ID: {{$node["Notion Trigger"].json.id}} Properties to update: Status = "Published" FB Post ID = {{$json.post_id}} Published = {{$now.toISO()}}

Token Management Strategy

✅ Option A: System User Token (Recommended)

Created in Business Manager → System Users. Never expires. Set once in n8n credentials, forget about it. This is the production-grade approach.

// How to get it: Business Manager → Business Settings → System Users → Add System User (Admin role) → Assign Assets (your Page + Ad Account) → Generate Token → Select your App → Permissions: pages_manage_posts, pages_read_engagement, ads_management → Expiration: Never → Copy token → Store in n8n as HTTP Header Auth credential

⚠️ Option B: Long-Lived Token + Auto-Refresh

If System User isn't available. Exchange short-lived token → long-lived (60 days). Set up a cron workflow to refresh every 30 days.

// Token exchange endpoint: GET https://graph.facebook.com/v21.0/oauth/access_token ?grant_type=fb_exchange_token &client_id={APP_ID} &client_secret={APP_SECRET} &fb_exchange_token={SHORT_LIVED_TOKEN} // Returns: { "access_token": "long_lived_token", "expires_in": 5184000 } // Then get Page Token from long-lived user token: GET https://graph.facebook.com/v21.0/me/accounts ?access_token={LONG_LIVED_USER_TOKEN}
Facebook Ad Campaign Creator
Trigger: new row in Notion campaign DB → creates ad set, uploads creative, launches ad — all via Marketing API.

n8n Workflow Flow

1
Notion Trigger
New campaign row
2
Create Ad Set
Budget + targeting
3
Fetch Image
Download creative
4
Upload Image
To ad account
5
Create Creative
Ad creative object
6
Create Ad
Launch the ad
7
Update Notion
Log ad IDs
1

Notion Trigger — Campaign Database

Watches "GTM Campaigns" DB for rows with status = "Launch". Contains all ad parameters: budget, targeting, creative URL, copy.

2

HTTP Request — Create Ad Set

Creates an ad set under the existing campaign. Defines budget, schedule, targeting, and optimization goal.

// n8n Node: HTTP Request Method: POST URL: https://graph.facebook.com/v21.0/act_{AD_ACCOUNT_ID}/adsets Body (form-data): name = {{$json["Ad Set Name"]}} campaign_id = {{$json["Campaign ID"]}} // Parent campaign daily_budget = {{$json["Daily Budget"] * 100}} // In cents! $10 = 1000 billing_event = "IMPRESSIONS" optimization_goal = "LINK_CLICKS" // or CONVERSIONS, REACH bid_strategy = "LOWEST_COST_WITHOUT_CAP" status = "PAUSED" // Start paused for safety start_time = {{$json["Start Date"]}} // ISO 8601 // Targeting (JSON string): targeting = { "age_min": {{$json["Age Min"] || 25}}, "age_max": {{$json["Age Max"] || 55}}, "genders": [0], // 0=all, 1=male, 2=female "geo_locations": { "countries": ["TH"] // or ["US","GB"] etc. }, "flexible_spec": [{ "interests": [{"id": "{{$json.Interest_ID}}", "name": "{{$json.Interest_Name}}"}] }] } access_token = {{$credentials.fbSystemToken}} Response: { "id": "adset_id_here" }
3

HTTP Request — Fetch Creative Image

Downloads the ad image from the URL stored in Notion. Returns binary data for upload.

// n8n Node: HTTP Request Method: GET URL: {{$json["Creative Image URL"]}} Response Format: File (binary) Output Field: data
4

HTTP Request — Upload Ad Image

Uploads the image to the ad account's image library. Returns a hash used in the creative.

// n8n Node: HTTP Request Method: POST URL: https://graph.facebook.com/v21.0/act_{AD_ACCOUNT_ID}/adimages Body (form-data): filename = ad_image.jpg // Send as multipart file access_token = {{$credentials.fbSystemToken}} Response: { "images": { "ad_image.jpg": { "hash": "abc123...", "url": "..." } } } // Save the hash → used in next step
5

HTTP Request — Create Ad Creative

Combines the uploaded image with ad copy, headline, CTA button, and link to create the ad creative object.

// n8n Node: HTTP Request Method: POST URL: https://graph.facebook.com/v21.0/act_{AD_ACCOUNT_ID}/adcreatives Body (JSON): { "name": "{{$json["Ad Name"]}} Creative", "object_story_spec": { "page_id": "{PAGE_ID}", "link_data": { "image_hash": "{{$node['Upload Image'].json.images['ad_image.jpg'].hash}}", "link": "{{$json['Landing Page URL']}}", "message": "{{$json['Ad Copy']}}", "name": "{{$json['Headline']}}", "description": "{{$json['Description']}}", "call_to_action": { "type": "{{$json['CTA'] || 'LEARN_MORE'}}", "value": { "link": "{{$json['Landing Page URL']}}" } } } }, "access_token": "{{$credentials.fbSystemToken}}" } Response: { "id": "creative_id_here" }
6

HTTP Request — Create Ad

Creates the actual ad by linking the creative to the ad set. Starts in PAUSED status for review.

// n8n Node: HTTP Request Method: POST URL: https://graph.facebook.com/v21.0/act_{AD_ACCOUNT_ID}/ads Body (form-data): name = {{$json["Ad Name"]}} adset_id = {{$node['Create Ad Set'].json.id}} creative = {"creative_id": "{{$node['Create Creative'].json.id}}"} status = "PAUSED" // PAUSED for safety, activate manually or via API access_token = {{$credentials.fbSystemToken}} Response: { "id": "ad_id_here" }
7

Notion Update — Log All IDs

Updates the campaign row in Notion with all created IDs for tracking and management.

// n8n Node: Notion (Update Page) Page ID: {{$node["Notion Trigger"].json.id}} Properties: Status = "Live (Paused)" Ad Set ID = {{$node['Create Ad Set'].json.id}} Ad ID = {{$node['Create Ad'].json.id}} Creative ID = {{$node['Create Creative'].json.id}} Launched At = {{$now.toISO()}}
Notion Database Schemas
Two databases power the entire system. These will be created in Fasai's Notion workspace.

📝 GTM Posts Database

PropertyType
Post NameTitle
CaptionRich Text
Image URLURL
PlatformSelect: FB, IG, Both
StatusStatus: Draft → Ready to Publish → Published → Error
BusinessSelect: CC, DVC, FJ, Other
Page IDText (FB Page ID)
FB Post IDText (auto-filled)
Published AtDate (auto-filled)
Schedule ForDate (optional future post)

📊 GTM Campaigns Database

PropertyType
Campaign NameTitle
Ad CopyRich Text
HeadlineText
DescriptionText
Creative Image URLURL
Landing Page URLURL
CTASelect: LEARN_MORE, SHOP_NOW, SIGN_UP, BOOK_TRAVEL, CONTACT_US
Daily BudgetNumber (USD)
Age Min / Age MaxNumber
CountriesText (comma-sep codes)
Interest ID / NameText
Campaign IDText (parent campaign)
StatusStatus: Draft → Launch → Live → Error
BusinessSelect: CC, DVC, FJ, Other
Ad Set IDText (auto-filled)
Ad IDText (auto-filled)
Launched AtDate (auto-filled)
Execution Plan
Build Sequence
What happens in what order. Estimated total: ~3 hours with credentials ready.
Can Do Now
Phase 1

Create Notion Databases

Build "GTM Posts" and "GTM Campaigns" databases in Notion with all properties. No credentials needed. Can be done immediately.

~15 min
Needs Creds
Phase 2

Build FB Posting Workflow in n8n

Create the n8n workflow: Notion trigger → validate → post to FB → update Notion. Needs Page Access Token and Page ID to configure.

~45 min
Needs Creds
Phase 3

Build FB Ads Workflow in n8n

Create the n8n workflow: Notion trigger → create ad set → upload image → create creative → create ad → update Notion. Needs Ad Account ID, Campaign ID, Pixel ID.

~1 hr
Needs Creds
Phase 4

Test + Activate

Test both workflows with real data. Post goes live on FB page. Ad creates in paused state. Verify all Notion fields update correctly.

~45 min
Built-in Safeguards
🛑

Ads Start Paused

All ads are created with status: PAUSED. Nothing goes live until explicitly activated — either manually in Ads Manager or via a separate activation workflow.

Notion as Gate

Nothing fires until a human sets the status to "Ready to Publish" or "Launch" in Notion. This is the human approval gate.

📋

Full Audit Trail

Every action writes back to Notion: FB Post IDs, Ad IDs, timestamps, error messages. Complete paper trail for every execution.

Phase 1 Starts Now

Creating the Notion databases doesn't need any Facebook credentials. Once Fasai provides the tokens and IDs, the n8n workflows can be built in one session.