FS-DOC-05Technical
Integration and API Spec
Social Media Scheduling
[YourCompany.com] · Marketing Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative technical reference for every integration point in the Social Media Scheduling automation. It covers the full tool inventory, per-tool authentication and scope requirements, exact field mappings between agent handoffs, the orchestration layout and credential store structure, and defined error handling behaviour for every failure scenario. The FullSpec team uses this specification throughout the build. No integration credential should be hardcoded; every secret must live in the shared credential store as described in Section 04.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Airtable
Content calendar trigger, status updates, feedback logging
Personal Access Token (PAT) via OAuth 2.0
Team ($20/mo)
Agent 1, Agent 2, Agent 3
Google Drive
Caption doc and image asset retrieval
OAuth 2.0 (service account)
Free (Google Workspace or personal)
Agent 1
Slack
Approval request delivery, interactive button capture, weekly summary
OAuth 2.0 (Slack App with Bot Token)
Free plan (note history limit); Pro recommended
Agent 2, Agent 3
Buffer
Multi-channel post scheduling via REST API
OAuth 2.0 (Buffer access token)
Essentials ($18/mo)
Agent 3
Canva
Graphic creation and export (manual step; no API integration in this build)
N/A
Pro ($13/mo)
None (manual)
Automation platform
Workflow orchestration, credential store, trigger management, retry logic
Internal (platform credentials)
Paid tier supporting webhooks and multi-step workflows ($49/mo)
All agents
Before you connect anything: create sandbox or test variants of every integration (Airtable test base, Buffer sandbox profile, Slack test workspace or private channel, Google Drive test folder) and validate all credential flows in those environments before switching to production credentials. Never store or test with production API keys during development.
02Per-tool integration detail
Airtable
Used by all three agents. Agent 1 polls or receives webhook events when a record status changes to 'Ready for Review'. Agent 2 logs rejection feedback and resets record status. Agent 3 updates record status to 'Scheduled' and writes the confirmed Buffer post URL back to the record.
Auth method
OAuth 2.0 using a Personal Access Token (PAT). Generate the PAT from the Airtable developer hub under the service account. Store as AIRTABLE_PAT in the credential store. Do not use legacy API keys.
Required scopes
data.records:read | data.records:write | webhook:manage | schema.bases:read
Webhook / trigger setup
Create a webhook on the content calendar base targeting the 'Content Calendar' table. Filter for the event type 'update:record' where the field 'Status' transitions to 'Ready for Review'. Webhook endpoint is provided by the automation platform on workflow creation. Validate the Airtable webhook ping with HMAC-SHA256 using the webhook secret stored as AIRTABLE_WEBHOOK_SECRET.
Required configuration
Base ID stored as AIRTABLE_BASE_ID. Table ID stored as AIRTABLE_TABLE_ID. Field names must match exactly: 'Status' (single select), 'Caption' (long text), 'Asset URL' (URL), 'Target Channels' (multi-select), 'Scheduled Time' (date/time), 'Feedback Notes' (long text), 'Buffer Post URL' (URL). All IDs stored in credential store, not hardcoded.
Rate limits
5 requests/second per base on the Team plan. At 40 to 80 posts/month (roughly 3 records/day peak), the automation will never approach this ceiling. No throttling required at current volume. If volume scales past 300 records/month, introduce a 250ms inter-request delay.
Constraints
Webhook deliveries are not guaranteed; implement idempotency checks using the Airtable record ID to prevent duplicate processing. Webhook retries from Airtable use exponential backoff up to 72 hours; the platform must acknowledge with HTTP 200 within 10 seconds or Airtable will retry.
// Webhook payload (incoming)
{
"webhook_id": "<string>",
"timestamp": "<ISO8601>",
"changedTablesById": {
"<AIRTABLE_TABLE_ID>": {
"changedRecordsById": {
"<record_id>": {
"current": { "cellValuesByFieldId": { "fldStatus": "Ready for Review" } }
}
}
}
}
}
// Write back (Agent 3 update)
PATCH /v0/{AIRTABLE_BASE_ID}/{AIRTABLE_TABLE_ID}/{record_id}
{ "fields": { "Status": "Scheduled", "Buffer Post URL": "<url>" } }Google Drive
Used by Agent 1 only. Retrieves the caption document and image asset associated with a given Airtable content record. The Airtable record stores direct Google Drive file URLs; the integration resolves those URLs to readable content using the Drive API.
Auth method
OAuth 2.0 using a service account with domain-wide delegation disabled. JSON key file for the service account is stored as GDRIVE_SERVICE_ACCOUNT_JSON in the credential store. The service account email must be granted at least 'Viewer' access to the shared content asset folder in Google Drive.
Required scopes
https://www.googleapis.com/auth/drive.readonly | https://www.googleapis.com/auth/drive.metadata.readonly
Webhook / trigger setup
No Drive webhook is used. Agent 1 reads asset URLs directly from the Airtable record and calls the Drive API on demand. If a future build requires change notifications, use the Drive API push notification channel (drive.files.watch) with a 7-day channel expiry and renewal job.
Required configuration
GDRIVE_ROOT_FOLDER_ID stored in the credential store. This is the folder ID of the shared content asset folder. Caption text files and image assets must reside within this folder tree. File IDs are resolved from the 'Asset URL' field on the Airtable record; the URL format is https://drive.google.com/file/d/{FILE_ID}/view.
Rate limits
Drive API: 1,000 requests/100 seconds per user; 10 requests/second per user. At current volume (3 records/day peak, 2 Drive calls per record) the automation sits well under limit. No throttling required. Apply exponential backoff on any HTTP 429 response.
Constraints
Assets must be stored as direct Drive files, not as Shortcuts. Shared Drive (Team Drive) access requires the supportsAllDrives=true query parameter on all files.list and files.get calls. The service account must be added as a member of any Shared Drive in use.
// Fetch file metadata
GET https://www.googleapis.com/drive/v3/files/{FILE_ID}
?fields=id,name,mimeType,webContentLink
&supportsAllDrives=true
Authorization: Bearer {GDRIVE_ACCESS_TOKEN}
// Fetch caption text content (Google Doc exported as plain text)
GET https://www.googleapis.com/drive/v3/files/{FILE_ID}/export
?mimeType=text/plain
// Output passed to Agent 2
{
"caption_text": "<string>",
"image_url": "<direct_download_url>",
"file_name": "<string>",
"mime_type": "image/jpeg | image/png"
}Integration and API SpecPage 1 of 4
FS-DOC-05Technical
Slack
Used by Agent 2 (approval request with interactive buttons and response capture) and Agent 3 (weekly schedule summary post). Requires a custom Slack App installed to the workspace with bot and interactive component permissions enabled.
Auth method
OAuth 2.0. Install the Slack App to the workspace via the Slack App Directory OAuth flow. Store the Bot User OAuth Token as SLACK_BOT_TOKEN. Store the Signing Secret as SLACK_SIGNING_SECRET for request signature validation on all incoming payloads.
Required scopes
chat:write | chat:write.public | channels:read | incoming-webhook | commands | interactivity (enabled in App settings)
Webhook / trigger setup
Enable Interactivity in the Slack App settings. Set the Request URL to the automation platform's inbound webhook endpoint for approval responses. The platform must validate every incoming Slack payload by computing HMAC-SHA256 over 'v0:{slack_request_timestamp}:{raw_body}' using SLACK_SIGNING_SECRET and comparing to the X-Slack-Signature header. Reject any payload where the timestamp is older than 5 minutes.
Required configuration
SLACK_APPROVAL_CHANNEL_ID: the channel where approval requests are posted (stored in credential store). SLACK_SUMMARY_CHANNEL_ID: the channel for weekly schedule summaries. SLACK_BOT_TOKEN: bot token for all API calls. SLACK_SIGNING_SECRET: signature validation. Approval timeout window stored as APPROVAL_TIMEOUT_HOURS (default 24). All values in the credential store; none hardcoded.
Rate limits
Slack Tier 3 methods (chat.postMessage): 50 requests/minute. At peak volume (80 posts/month, one approval request per post), the automation sends fewer than 3 messages/day. No throttling required. The weekly summary is a single message. Apply retry with 1-second delay on HTTP 429.
Constraints
Free Slack plan limits message history to 90 days; the approval audit trail stored in Airtable is the system of record, not Slack. Interactive components (approve/reject buttons) use Block Kit; the action_id values for the buttons must be exactly 'approve_post' and 'reject_post' to match the platform's routing logic. If no response is received within APPROVAL_TIMEOUT_HOURS, the automation sets the Airtable record status to 'Pending Review' and posts a reminder to the coordinator.
// Approval request message payload (chat.postMessage)
POST https://slack.com/api/chat.postMessage
{
"channel": "{SLACK_APPROVAL_CHANNEL_ID}",
"blocks": [
{ "type": "section", "text": { "type": "mrkdwn", "text": "*New post ready for approval*\n{caption_text}" } },
{ "type": "image", "image_url": "{image_url}", "alt_text": "Post asset" },
{ "type": "section", "fields": [
{ "type": "mrkdwn", "text": "*Channels:* {target_channels}" },
{ "type": "mrkdwn", "text": "*Scheduled:* {scheduled_time}" }
]},
{ "type": "actions", "elements": [
{ "type": "button", "text": { "type": "plain_text", "text": "Approve" }, "style": "primary", "action_id": "approve_post", "value": "{record_id}" },
{ "type": "button", "text": { "type": "plain_text", "text": "Reject" }, "style": "danger", "action_id": "reject_post", "value": "{record_id}" }
]}
]
}
// Incoming interactive payload (approve or reject)
{
"type": "block_actions",
"actions": [{ "action_id": "approve_post | reject_post", "value": "{record_id}" }],
"user": { "id": "<slack_user_id>", "name": "<slack_username>" }
}Buffer
Used by Agent 3 only. Receives approved content packages and schedules posts to the configured social channels via the Buffer Publishing API. Each Airtable content record maps to one or more Buffer updates depending on the number of target channels.
Auth method
OAuth 2.0. Complete the Buffer OAuth flow to obtain a long-lived access token. Store as BUFFER_ACCESS_TOKEN in the credential store. Buffer access tokens do not expire unless revoked; implement a token validity check on startup and alert via Slack if the token is invalid.
Required scopes
publish (required for creating and scheduling updates) | The Buffer API does not use granular scope strings beyond this; the access token grants access to all profiles associated with the authenticated Buffer account.
Webhook / trigger setup
No inbound webhook from Buffer is used in this build. The automation pushes to Buffer; it does not receive events from Buffer. If post-publish confirmation is required in a future build, Buffer's Zapier/webhook integration or polling the updates endpoint can provide status.
Required configuration
BUFFER_ACCESS_TOKEN: stored in credential store. BUFFER_PROFILE_IDS: a JSON object mapping channel names (e.g. 'instagram', 'linkedin', 'twitter', 'facebook') to their Buffer profile IDs. Example: { "instagram": "abc123", "linkedin": "def456" }. Stored as BUFFER_PROFILE_MAP in the credential store. The 'Target Channels' multi-select field in Airtable must use values that exactly match the keys in BUFFER_PROFILE_MAP. LinkedIn and TikTok require additional OAuth scope confirmation before go-live; see implementation notes.
Rate limits
Buffer Publishing API: 60 requests/minute on the Essentials plan. At 40 to 80 posts/month across 3 to 5 channels, peak API calls are approximately 400 per month (80 posts x 5 channels), averaging fewer than 2 calls/minute. No throttling required at current volume. Apply retry with exponential backoff (1s, 2s, 4s) on HTTP 429 or HTTP 503.
Constraints
LinkedIn scheduling via Buffer requires a connected LinkedIn Page (not personal profile) and the Buffer account must have the LinkedIn integration active. TikTok scheduling is subject to Buffer's TikTok API access tier; confirm availability before build. Buffer's free plan does not support API access; the Essentials plan ($18/mo) is the minimum. Scheduled time must be provided as a Unix timestamp in UTC.
// Schedule a post (POST /v1/updates/create.json)
POST https://api.bufferapp.com/1/updates/create.json
Authorization: Bearer {BUFFER_ACCESS_TOKEN}
{
"profile_ids": ["{BUFFER_PROFILE_ID_1}", "{BUFFER_PROFILE_ID_2}"],
"text": "{caption_text}",
"media": {
"photo": "{image_public_url}",
"thumbnail": "{image_public_url}"
},
"scheduled_at": "{unix_timestamp_utc}",
"now": false
}
// Successful response
{
"success": true,
"buffer_count": 1,
"updates": [{ "id": "<buffer_update_id>", "status": "buffer", "due_at": <unix_ts> }]
}Integration and API SpecPage 2 of 4
FS-DOC-05Technical
03Field mappings between tools
The following tables define the exact field mappings for each agent handoff in the pipeline. Use monospace field names exactly as they appear; any mismatch will cause a silent mapping failure or a null value in the destination.
Handoff 1: Airtable to Google Drive (Content Intake Agent, Agent 1)
Source tool
Source field
Destination tool
Destination field
Airtable
`record_id`
Internal (pipeline context)
`context.record_id`
Airtable
`Status`
Internal (pipeline context)
`context.status`
Airtable
`Asset URL`
Google Drive API
`files.get.fileId` (parsed from URL)
Airtable
`Caption`
Internal (pipeline context)
`context.caption_text`
Airtable
`Target Channels`
Internal (pipeline context)
`context.target_channels[]`
Airtable
`Scheduled Time`
Internal (pipeline context)
`context.scheduled_time_iso`
Google Drive
`files.name`
Internal (pipeline context)
`context.asset_file_name`
Google Drive
`webContentLink`
Internal (pipeline context)
`context.asset_download_url`
Handoff 2: Content Intake Agent output to Approval Routing Agent (Agent 1 to Agent 2)
Source tool
Source field
Destination tool
Destination field
Internal context
`context.record_id`
Slack (Block Kit)
`actions[].value`
Internal context
`context.caption_text`
Slack (Block Kit)
`blocks[0].text.text`
Internal context
`context.asset_download_url`
Slack (Block Kit)
`blocks[1].image_url`
Internal context
`context.target_channels[]`
Slack (Block Kit)
`blocks[2].fields[0].text`
Internal context
`context.scheduled_time_iso`
Slack (Block Kit)
`blocks[2].fields[1].text`
Internal context
`context.record_id`
Airtable
`record_id` (for feedback write-back on rejection)
Handoff 3: Approval Routing Agent output to Publishing and Reporting Agent (Agent 2 to Agent 3)
Source tool
Source field
Destination tool
Destination field
Slack (interactive payload)
`actions[0].action_id`
Internal (router)
`context.approval_decision`
Slack (interactive payload)
`actions[0].value`
Internal (router)
`context.record_id`
Slack (interactive payload)
`user.name`
Airtable
`Approved By` (text field)
Internal context
`context.caption_text`
Buffer API
`text`
Internal context
`context.asset_download_url`
Buffer API
`media.photo`
Internal context
`context.target_channels[]`
Buffer API
`profile_ids[]` (resolved via BUFFER_PROFILE_MAP)
Internal context
`context.scheduled_time_iso`
Buffer API
`scheduled_at` (converted to Unix timestamp UTC)
Buffer API response
`updates[0].id`
Airtable
`Buffer Post URL` (constructed as https://buffer.com/updates/{id})
Buffer API response
`updates[0].status`
Airtable
`Status` (set to 'Scheduled')
The 'Target Channels' multi-select values in Airtable must exactly match the keys defined in BUFFER_PROFILE_MAP (e.g. 'instagram', 'linkedin', 'twitter', 'facebook'). Any value not found in the map will be skipped silently. Validate this mapping during the Discovery and Tool Access Setup stage before build begins.
04Build stack and orchestration
Orchestration layout
Three discrete workflows, one per agent. Each workflow is independently deployable and testable. Shared credential store is referenced by all three workflows. Workflows are chained via internal event passing (record_id in context), not direct function calls.
Agent 1 trigger (Content Intake Agent)
Webhook. Airtable fires a webhook on record status change to 'Ready for Review'. The automation platform exposes a dedicated HTTPS endpoint for this webhook. Signature validation via HMAC-SHA256 using AIRTABLE_WEBHOOK_SECRET is performed on every inbound request before processing begins. Idempotency key: Airtable record_id stored in a short-term deduplication cache (TTL 24 hours).
Agent 2 trigger (Approval Routing Agent)
Webhook. Slack posts an interactive component payload to the platform's inbound endpoint when the reviewer clicks Approve or Reject. Signature validation via HMAC-SHA256 using SLACK_SIGNING_SECRET is mandatory. A separate scheduled poll runs every APPROVAL_TIMEOUT_HOURS to identify records still in 'Pending Approval' status and dispatch reminder messages.
Agent 3 trigger (Publishing and Reporting Agent)
Internal event. Triggered when Agent 2 routes an approved record. The weekly Slack summary uses a scheduled cron trigger (Monday 08:00 local time, configurable) that queries Airtable for all records with Status 'Scheduled' created in the prior 7 days and composes the summary message.
Credential store
All secrets and environment-specific IDs are stored in the automation platform's built-in encrypted credential store. No secret appears in workflow logic, code nodes, or documentation in plaintext. See code block below for the full contents list.
Credential store reference: all secrets required by the three-agent pipeline
// Credential store contents
// All values are injected at runtime via the platform credential manager.
// Never commit these values to version control or hardcode in workflow nodes.
AIRTABLE_PAT // Airtable Personal Access Token
AIRTABLE_WEBHOOK_SECRET // HMAC secret for Airtable webhook signature validation
AIRTABLE_BASE_ID // Base ID of the content calendar Airtable base
AIRTABLE_TABLE_ID // Table ID of the 'Content Calendar' table
GDRIVE_SERVICE_ACCOUNT_JSON // Full JSON key file for the Google Drive service account
GDRIVE_ROOT_FOLDER_ID // Folder ID of the shared content asset folder in Google Drive
SLACK_BOT_TOKEN // Bot User OAuth Token for all Slack API calls
SLACK_SIGNING_SECRET // Secret for validating inbound Slack interactive payloads
SLACK_APPROVAL_CHANNEL_ID // Channel ID where approval request messages are posted
SLACK_SUMMARY_CHANNEL_ID // Channel ID where weekly schedule summary is posted
APPROVAL_TIMEOUT_HOURS // Number of hours before an unanswered approval triggers a reminder (default: 24)
BUFFER_ACCESS_TOKEN // Long-lived OAuth access token for Buffer Publishing API
BUFFER_PROFILE_MAP // JSON object mapping channel names to Buffer profile IDs
// e.g. { "instagram": "abc123", "linkedin": "def456",
// "twitter": "ghi789", "facebook": "jkl012" }Integration and API SpecPage 3 of 4
FS-DOC-05Technical
05Error handling and retry logic
Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently. Where a retry sequence is exhausted without success, the workflow must write a failure status to the Airtable record and post an alert to the Slack coordinator channel.
Integration
Scenario
Required behaviour
Airtable webhook
Webhook payload fails HMAC-SHA256 signature validation
Reject immediately with HTTP 403. Log the rejection with timestamp and raw signature values. Do not process the payload. Alert the FullSpec monitoring channel.
Airtable webhook
Duplicate webhook delivery for the same record_id
Check deduplication cache (TTL 24h). If record_id is already present, acknowledge with HTTP 200 and take no action. Log the duplicate event for audit.
Airtable API (read)
HTTP 429 rate limit exceeded on record fetch
Retry with exponential backoff: 1s, 2s, 4s, 8s (max 4 retries). If all retries fail, set record status to 'Processing Error' and notify coordinator via Slack.
Airtable API (write)
HTTP 5xx error on status update or feedback write-back
Retry 3 times with 2s, 4s, 8s backoff. If all fail, log the attempted write payload to the platform error log and post a manual fallback alert to the coordinator: 'Unable to update record {record_id}. Please update manually.'
Google Drive API
Asset file not found or access denied (HTTP 404 or 403)
Set Airtable record status to 'Asset Error'. Post alert to coordinator Slack channel with the record ID and the file URL that failed. Do not proceed to approval step. Coordinator must re-link the asset and reset status.
Google Drive API
HTTP 429 quota exceeded on file retrieval
Retry with exponential backoff: 1s, 2s, 4s (max 3 retries). If quota is sustained, pause the workflow for 60 seconds before a final retry. On total failure, set Airtable status to 'Asset Error' and alert coordinator.
Slack API (approval message)
chat.postMessage fails (HTTP 4xx or 5xx)
Retry up to 3 times with 1s delay. If all fail, set Airtable record to 'Approval Pending (Manual)' and send an email fallback notification to the configured FALLBACK_APPROVER_EMAIL address with the content package details.
Slack interactive payload
Incoming approval response fails timestamp check (replay attack, timestamp older than 5 minutes)
Reject with HTTP 400. Log the event. Do not update the Airtable record. The approval message in Slack remains active for the reviewer to click again.
Slack interactive payload
No approval response received within APPROVAL_TIMEOUT_HOURS
Scheduled poll detects the stale record. Post a reminder message to the approval channel tagging the reviewer. Update Airtable 'Feedback Notes' to 'Awaiting approval reminder sent {timestamp}'. If no response for a further 24 hours, escalate to SLACK_SUMMARY_CHANNEL_ID.
Buffer API (schedule post)
HTTP 429 rate limit or HTTP 503 service unavailable
Retry with exponential backoff: 1s, 2s, 4s (max 3 retries). If all fail, set Airtable record to 'Scheduling Failed'. Post alert to coordinator Slack channel: 'Buffer scheduling failed for record {record_id}. Manual scheduling required.' Include caption text and target channels in the alert.
Buffer API (schedule post)
Channel not found in BUFFER_PROFILE_MAP (invalid target channel value from Airtable)
Skip the unmapped channel. Log a warning to the platform error log. Continue scheduling for all valid channels. Post a Slack warning to the coordinator listing the skipped channel name so it can be added to BUFFER_PROFILE_MAP or corrected in the Airtable record.
Buffer API token
BUFFER_ACCESS_TOKEN is revoked or returns HTTP 401
Immediately halt all Agent 3 processing. Post a high-priority alert to the Slack coordinator channel and to support@gofullspec.com: 'Buffer access token is invalid. Re-authenticate and update BUFFER_ACCESS_TOKEN in the credential store before workflows can resume.' Set all pending 'Scheduled' Airtable records to 'Publishing Paused'.
Global rule: any exception not matched by the above scenarios must be caught by a top-level error handler in each workflow. The handler must write the error type, message, and timestamp to the platform error log, update the relevant Airtable record to 'Processing Error', and post a Slack alert to the coordinator channel. No exception may be swallowed. Contact support@gofullspec.com if an unclassified error pattern recurs more than twice in a 24-hour window.
Integration and API SpecPage 4 of 4