Back to Social Media Scheduling

Developer Handover Pack

Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.

4 pagesPDF · Technical
FS-DOC-04Technical

Developer Handover Pack

Social Media Scheduling

[YourCompany.com] · Marketing Department · Prepared by FullSpec · [Today's Date]

This document is the primary technical reference for the FullSpec team building the Social Media Scheduling automation. It covers the full current-state process map, all three agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. The FullSpec team owns everything in this document from first commit through to handover. The process owner and social media coordinator are responsible for confirming Airtable schema, supplying API credentials, and approving test content runs before go-live.

01Process snapshot

Step
Name
Description
1
Pull Content Brief From Calendar
Coordinator opens the Airtable content calendar and identifies posts due for the coming week. Time cost: 15 min/cycle.
2
Write or Retrieve Captions
Captions are written from scratch in a Google Doc or retrieved from a prior draft and formatted per platform. Time cost: 40 min/cycle.
3 BOTTLENECK
Export and Resize Images in Canva
Each graphic is resized to every target platform's spec in Canva and downloaded individually. Time cost: 35 min/cycle.
4
Organise Assets in Shared Drive
Downloaded images are renamed and placed in the correct Google Drive folder, matched to captions. Time cost: 20 min/cycle.
5 BOTTLENECK
Send Draft for Approval via Slack
A Slack message with screenshots or document links is sent to the approver. Follow-up chases are common and approvals can stall for 1 to 3 days. Time cost: 10 min/cycle (plus unbounded wait time).
6
Incorporate Feedback and Revise
Revision requests are tracked in the Slack thread, applied to the caption or image, and resubmitted. Time cost: 25 min/cycle.
7 BOTTLENECK
Upload and Schedule Posts in Buffer
Coordinator logs into Buffer, creates each post manually, uploads the correct image, pastes the caption, and sets the publish time per channel. Time cost: 45 min/cycle.
8
Confirm Schedule and Notify Team
A Slack message confirms the week's posts are live in the queue with a summary of scheduled times. Time cost: 10 min/cycle.
Time cost summary: Total manual time per cycle is 200 minutes (3 hours 20 min). At a cadence of approximately 1.5 content batches per week, this equates to roughly 6 hours of manual effort per week, or approximately 26 hours per 30-day period. The automation replaces steps 1, 4, 5, 7, and 8 entirely. Steps 2 and 6 remain manual: the coordinator still authors captions and applies revisions after a rejection. Step 3 (Canva export) also remains manual in this build scope.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Content Intake Agent

Monitors the Airtable content calendar for records whose status field transitions to 'Ready for Review'. On detection, it reads the caption text field and the Google Drive asset link field from the same record, retrieves the referenced file metadata from Google Drive to confirm the asset is accessible, and assembles a structured content package object containing all fields required by the Approval Routing Agent. If the Drive file is missing or inaccessible, the agent sets the Airtable record status to 'Asset Error' and halts the run for that record without proceeding downstream.

Trigger
Airtable record status field changes to 'Ready for Review' via Airtable webhook (or polling interval of 5 minutes as fallback).
Tools
Airtable (content calendar base), Google Drive (asset retrieval)
Replaces steps
Step 1 (Pull Content Brief) and Step 4 (Organise Assets in Shared Drive)
Estimated build
8 hours — Moderate
// Input
airtable.record_id         : string   // Airtable record unique ID
airtable.post_name         : string   // Human-readable post title
airtable.caption_text      : string   // Full caption body, pre-formatted
airtable.target_channels   : string[] // e.g. ['instagram','facebook','linkedin']
airtable.scheduled_time    : ISO8601  // Desired publish timestamp
airtable.drive_asset_url   : string   // Google Drive file share link

// Output
content_package.record_id       : string
content_package.post_name       : string
content_package.caption_text    : string
content_package.target_channels : string[]
content_package.scheduled_time  : ISO8601
content_package.asset_url       : string   // Confirmed accessible Drive URL
content_package.assembled_at    : ISO8601  // Timestamp of package creation

// On error (asset not found)
airtable.status -> 'Asset Error'
airtable.error_note -> 'Drive file unreachable at [timestamp]'
  • Airtable plan must be on the Team tier or above to enable webhook automations. The Free and Plus tiers do not support outbound webhooks. Confirm the plan before build starts.
  • The Airtable base field for status must use a Single Select field type named exactly 'Status'. Any variation in field name requires a config update in the trigger node.
  • Google Drive retrieval uses a service account with read-only access scoped to the specific shared folder. OAuth scopes required: drive.readonly. The service account JSON key must be stored in the shared credential store, not hardcoded.
  • The agent must deduplicate on record_id. If the same record fires the trigger twice (e.g. status toggled back and forth), the second run must be suppressed using a processed_ids log in the automation platform's data store.
  • Confirm the Drive asset field always contains a direct file link (not a folder link) before build. A folder link cannot be resolved to a single asset without additional logic.
  • Image resizing is out of scope for this build. The coordinator must upload correctly sized assets to Drive before setting status to 'Ready for Review'.
Approval Routing Agent

Receives the content package from the Content Intake Agent and posts a structured interactive message to the designated Slack approvals channel. The message includes the post name, formatted caption, target channels, scheduled publish time, and the Google Drive asset URL as a linked thumbnail. The message presents two interactive button actions: Approve and Reject. On Approve, the agent updates the Airtable record status to 'Approved' and passes the content package to the Publishing and Reporting Agent. On Reject, it captures the reviewer's comment from the Slack modal, writes the feedback as a note to the Airtable record, and sets the record status to 'In Revision'. If no response is received within the configured timeout window (default 48 hours), the agent sends a reminder to the coordinator and leaves the record in 'Pending Approval' status.

Trigger
Content Intake Agent delivers a validated content_package object.
Tools
Slack (interactive messages and block kit), Airtable (status and feedback logging)
Replaces steps
Step 5 (Send Draft for Approval via Slack) and Step 6 (Incorporate Feedback and Revise)
Estimated build
10 hours — Complex
// Input (from Content Intake Agent)
content_package.record_id       : string
content_package.post_name       : string
content_package.caption_text    : string
content_package.target_channels : string[]
content_package.scheduled_time  : ISO8601
content_package.asset_url       : string

// Output (on Approve)
approved_package.record_id      : string
approved_package.caption_text   : string
approved_package.target_channels: string[]
approved_package.scheduled_time : ISO8601
approved_package.asset_url      : string
approved_package.approved_by    : string   // Slack user display name
approved_package.approved_at    : ISO8601
airtable.status                 -> 'Approved'

// Output (on Reject)
airtable.status                 -> 'In Revision'
airtable.reviewer_notes         -> string  // Feedback text from Slack modal
airtable.rejected_at            -> ISO8601

// On approval (downstream trigger for Publishing Agent)
approved_package -> Publishing and Reporting Agent input
  • The Slack app must have the following OAuth scopes enabled: chat:write, chat:write.public, channels:read, reactions:read, and interactivity must be switched on in the Slack app manifest. Confirm the workspace admin has approved the app before build begins.
  • Interactive message buttons require the Slack app's Request URL (interactivity endpoint) to be a publicly reachable HTTPS URL. The automation platform's webhook endpoint URL must be registered here during setup.
  • If the business is on Slack's free plan, message history is limited to 90 days. This may affect the approval audit trail for older records. Recommend upgrading to Pro or logging all decisions to Airtable as the primary audit record (which this agent does by design).
  • The rejection modal must use a Slack Block Kit plain_text_input element to capture reviewer feedback. Do not rely on Slack thread replies, as those cannot be reliably intercepted by a webhook listener.
  • The 48-hour timeout is a configurable environment variable (APPROVAL_TIMEOUT_HOURS). Set this in the credential/config store so it can be changed without a code deployment.
  • Dedupe logic: if the approver presses Approve more than once (e.g. double-click), the second event must be discarded. Check approved_package.record_id against a short-lived processed set before triggering the publishing agent.
Publishing and Reporting Agent

Receives an approved content package and submits it to Buffer via the Buffer API, setting the caption, asset URL, target channels, and scheduled publish time exactly as specified. After a successful Buffer API response, the agent writes the returned Buffer post ID and the confirmed publish URL back to the Airtable record and updates the record status to 'Scheduled'. Once per week, on a configurable schedule (default: Monday 08:00 in the workspace timezone), the agent queries Airtable for all records with status 'Scheduled' in the coming 7-day window and posts a formatted summary message to the designated Slack marketing channel listing each post's name, channel targets, and scheduled time with a link to the Buffer queue.

Trigger
Approval Routing Agent delivers an approved_package object (per-post); weekly cron schedule for the summary report.
Tools
Buffer (API scheduling), Airtable (status update and Buffer link logging), Slack (weekly summary message)
Replaces steps
Step 7 (Upload and Schedule Posts in Buffer) and Step 8 (Confirm Schedule and Notify Team)
Estimated build
10 hours — Moderate
// Input (from Approval Routing Agent)
approved_package.record_id       : string
approved_package.caption_text    : string
approved_package.target_channels : string[]   // Must match Buffer profile IDs
approved_package.scheduled_time  : ISO8601
approved_package.asset_url       : string
approved_package.approved_by     : string

// Output (on successful Buffer schedule)
airtable.status                  -> 'Scheduled'
airtable.buffer_post_id          -> string   // Buffer-returned post ID
airtable.buffer_post_url         -> string   // Direct link to Buffer queue entry
airtable.scheduled_confirmed_at  -> ISO8601

// Output (on Buffer API error)
airtable.status                  -> 'Publish Error'
airtable.error_note              -> string   // Buffer error message + HTTP status
slack.alert_channel              -> string   // Error notification to coordinator

// Output (weekly summary, cron trigger)
slack.weekly_summary_message : Block Kit message listing all 'Scheduled' posts
// Fields per post in summary:
//   post_name, target_channels[], scheduled_time, buffer_post_url
  • Buffer API authentication uses OAuth 2.0. The access token and refresh token must be stored in the shared credential store. Confirm that the Buffer account has the Essentials plan or above, as the free plan does not expose API scheduling endpoints.
  • LinkedIn and TikTok require additional platform-level OAuth authentication within Buffer beyond the standard Buffer API token. These must be confirmed and connected in the Buffer dashboard before build begins. If either platform is not yet connected in Buffer, posting to it will fail silently unless the agent validates the profile ID list at runtime.
  • The target_channels field in Airtable must store Buffer profile IDs, not human-readable platform names. A reference lookup table mapping platform names to Buffer profile IDs must be created and stored as a config object in the automation platform during the Discovery stage.
  • The weekly summary cron expression is configurable: SUMMARY_CRON_SCHEDULE environment variable, default '0 8 * * 1' (Monday 08:00). The timezone must be set explicitly to match the workspace locale.
  • On a Buffer API 429 (rate limit) response, implement exponential backoff with a maximum of 3 retries before setting the Airtable record to 'Publish Error' and alerting the coordinator in Slack.
  • The Buffer API image upload requires the asset to be a publicly accessible URL. Google Drive share links with restricted access will fail. The Content Intake Agent must verify the sharing permission is set to 'Anyone with the link can view' before assembling the content package.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow with field names at each agent handoff
// ─────────────────────────────────────────────────────────────
// TRIGGER
// ─────────────────────────────────────────────────────────────
SOURCE: Airtable webhook
  event.type              = 'record.updated'
  event.base_id           = '<airtable_base_id>'
  event.table_id          = '<content_calendar_table_id>'
  event.record_id         = 'rec_XXXXXXXXXXXXXX'
  event.changed_fields    = { Status: 'Ready for Review' }

// ─────────────────────────────────────────────────────────────
// AGENT 1: Content Intake Agent
// ─────────────────────────────────────────────────────────────
STEP 1.1  Airtable GET record by event.record_id
  -> record.fields.post_name         : string
  -> record.fields.caption_text      : string
  -> record.fields.target_channels   : string[]   // Buffer profile IDs
  -> record.fields.scheduled_time    : ISO8601
  -> record.fields.drive_asset_url   : string

STEP 1.2  Google Drive GET file metadata by drive_asset_url
  -> drive.file.id                   : string
  -> drive.file.webContentLink       : string     // Public download URL
  -> drive.file.permissionIds        : string[]   // Verify public access

STEP 1.3  Dedupe check: record_id NOT IN processed_ids_store
  IF duplicate -> halt, no downstream action
  IF asset inaccessible -> Airtable PATCH status = 'Asset Error', halt

STEP 1.4  Assemble content_package
  content_package = {
    record_id       : event.record_id,
    post_name       : record.fields.post_name,
    caption_text    : record.fields.caption_text,
    target_channels : record.fields.target_channels,
    scheduled_time  : record.fields.scheduled_time,
    asset_url       : drive.file.webContentLink,
    assembled_at    : now()
  }
  -> Append record_id to processed_ids_store

// ─────────────────────────────────────────────────────────────
// HANDOFF 1: Content Intake Agent -> Approval Routing Agent
// Payload: content_package (all fields above)
// ─────────────────────────────────────────────────────────────

// ─────────────────────────────────────────────────────────────
// AGENT 2: Approval Routing Agent
// ─────────────────────────────────────────────────────────────
STEP 2.1  Slack POST chat.postMessage to #approvals channel
  Block Kit message includes:
    -> content_package.post_name
    -> content_package.caption_text
    -> content_package.target_channels (formatted list)
    -> content_package.scheduled_time (formatted datetime)
    -> content_package.asset_url (linked thumbnail)
    -> action_id: 'approve_post' button
    -> action_id: 'reject_post' button (opens modal)
  Slack returns:
    -> slack.message_ts            : string  // Message timestamp / ID
  Airtable PATCH record.fields.status = 'Pending Approval'

STEP 2.2a  ON Slack interactive callback action_id = 'approve_post'
  -> slack.user.display_name       : string
  -> slack.action.action_ts        : ISO8601
  Dedupe: IF record_id already in approved_ids_store -> discard
  Airtable PATCH:
    record.fields.status           = 'Approved'
    record.fields.approved_by      = slack.user.display_name
    record.fields.approved_at      = slack.action.action_ts
  Assemble approved_package (content_package + approved_by + approved_at)
  -> HANDOFF 2

STEP 2.2b  ON Slack interactive callback action_id = 'reject_post'
  -> slack.modal.reviewer_notes    : string  // Plain text input from modal
  Airtable PATCH:
    record.fields.status           = 'In Revision'
    record.fields.reviewer_notes   = slack.modal.reviewer_notes
    record.fields.rejected_at      = now()
  -> HALT (coordinator manually revises and resets status to 'Ready for Review')

STEP 2.3  TIMEOUT: IF no callback within APPROVAL_TIMEOUT_HOURS (default 48)
  -> Slack POST reminder to coordinator Slack DM
  -> Airtable record.fields.status remains 'Pending Approval'

// ─────────────────────────────────────────────────────────────
// HANDOFF 2: Approval Routing Agent -> Publishing and Reporting Agent
// Payload: approved_package
//   { record_id, post_name, caption_text, target_channels,
//     scheduled_time, asset_url, approved_by, approved_at }
// ─────────────────────────────────────────────────────────────

// ─────────────────────────────────────────────────────────────
// AGENT 3: Publishing and Reporting Agent
// ─────────────────────────────────────────────────────────────
STEP 3.1  FOR EACH channel_id IN approved_package.target_channels
  Buffer POST /1/updates/create.json
    body: {
      text           : approved_package.caption_text,
      media.link     : approved_package.asset_url,
      profile_ids    : [channel_id],
      scheduled_at   : approved_package.scheduled_time
    }
  Response:
    -> buffer.update.id            : string  // Buffer post ID
    -> buffer.update.status_url    : string  // Direct Buffer queue link
  ON 429: exponential backoff, max 3 retries
  ON persistent failure:
    Airtable PATCH status = 'Publish Error', error_note = buffer.error
    Slack POST alert to coordinator

STEP 3.2  ON all channels successfully scheduled
  Airtable PATCH:
    record.fields.status                = 'Scheduled'
    record.fields.buffer_post_id        = buffer.update.id (last channel)
    record.fields.buffer_post_url       = buffer.update.status_url
    record.fields.scheduled_confirmed_at = now()

STEP 3.3  CRON: SUMMARY_CRON_SCHEDULE (default '0 8 * * 1')
  Airtable LIST records WHERE status = 'Scheduled'
    AND scheduled_time BETWEEN now() AND now() + 7 days
  Build Slack Block Kit weekly summary:
    FOR EACH record: post_name, target_channels, scheduled_time, buffer_post_url
  Slack POST to #marketing channel

// ─────────────────────────────────────────────────────────────
// END STATE
// Airtable record.fields.status = 'Scheduled'
// Buffer queue contains post for each target channel
// Slack #marketing has received weekly summary
// ─────────────────────────────────────────────────────────────
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool with one workflow per agent (three workflows total: Content Intake, Approval Routing, Publishing and Reporting). All three workflows share a single credential store. No credentials are hardcoded in workflow nodes. Use environment variables for all tokens, IDs, and configurable parameters.
Webhook configuration
Airtable webhook registered against the content calendar table, filtered to Status field changes only, pointing to the automation platform's inbound webhook URL for the Content Intake Agent workflow. The Slack interactivity Request URL points to the automation platform's inbound webhook URL for the Approval Routing Agent workflow. Both endpoints must be HTTPS and publicly reachable. Rotate webhook signing secrets every 90 days.
Templating approach
All Slack Block Kit message templates (approval request and weekly summary) are maintained as JSON template strings within the workflow, with placeholder tokens substituted at runtime from the content_package or approved_package fields. Do not use a separate templating service. Caption text must be HTML-entity-escaped before insertion to prevent Block Kit rendering errors.
Error logging
All workflow errors (asset retrieval failures, Buffer API errors, Slack delivery failures, timeout events) are written to a dedicated Supabase table: automation_error_log with columns: id (uuid), workflow_name (text), record_id (text), error_type (text), error_message (text), occurred_at (timestamptz), resolved (boolean). A Supabase database webhook or scheduled query triggers a Slack alert to the coordinator's DM when a new unresolved error row is inserted.
Testing approach
All agent workflows are built and validated in a sandbox environment first, using a duplicate Airtable base and a private Slack test channel. Buffer sandbox/staging is not available on standard plans; use a dedicated test Buffer profile connected to a dummy social account. No test runs are executed against live social profiles or the production Airtable base until the end-to-end test phase is signed off.
Estimated total build time
Content Intake Agent: 8 hours. Approval Routing Agent: 10 hours. Publishing and Reporting Agent: 10 hours. End-to-end integration testing and edge case validation: 8 hours (included in the 28-hour build effort total). Grand total: 28 hours.
Before build begins, the following must be confirmed: (1) Airtable base ID and table ID for the content calendar, with field names matching the spec above. (2) Buffer API access token and refresh token, with all target social profiles connected and profile IDs mapped to channel names. (3) Slack app created with interactivity enabled, Request URL registered, and bot token scoped to chat:write, chat:write.public, channels:read, and im:write. (4) Google Drive service account JSON key created with drive.readonly scope and shared to the content asset folder. (5) LinkedIn and TikTok authentication status within Buffer confirmed before those channels are included in target_channels. Contact support@gofullspec.com if any of these items are blocked.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Social Media Scheduling.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Integration and API Spec
Technical · Developer
View
Test and QA Plan
Quality · Developer
View