Case Study / Build 10
Social Scheduler AI Build
A focused internal publishing workflow for Greg Staunton's personal LinkedIn profile, supporting content connected with greg-staunton.com, Oracle Eloqua consulting, professional articles, credentials, and Pl8ypus AI build updates.
Greg's take
The point of this build was to solve a real operational problem in my own business. I already produce useful material through consulting work, articles, certifications, and AI builds. The gap was turning that material into a consistent and controlled publishing workflow without adding another heavyweight platform.
Building it against a real production integration forced the work beyond a polished mock-up. Authentication, token handling, scheduling, publishing failures, and operational visibility are all part of the product.
Keeping the tool limited to my own personal LinkedIn profile is deliberate. It keeps the current scope useful, supportable, and honest while giving me a practical system I can continue to operate.
Build milestones
A working foundation is deployed, with platform approval and hardening still in motion.
React operator interface
Compose, edit, delete and restore posts
Draft and scheduled states
Calendar view for scheduled posts
Cloudflare Worker API
D1-backed post storage
Two-minute cron scheduler
Two-phase publishing claim
Publish events and scheduler runs
LinkedIn OAuth admin flow for personal publishing
Cloudflare Access gate on API routes
Production route configuration
Complete LinkedIn app approval
Validate the production OAuth flow
Validate live publishing permissions
Strengthen token operations
Add richer audit and review controls
The problem
Good ideas do not automatically become consistent social publishing.
Drafts scatter quickly
Content ideas can start in notes, chat tools, emails, or rough prompts, then lose context before they become usable posts.
Publishing becomes repetitive
Formatting, checking links, deciding timing, and tracking what has gone out are small tasks that create drag when repeated manually.
The queue needs visibility
A contractor workflow needs a simple view of draft, scheduled, posted, failed, and recently deleted content without opening a large marketing suite.
Review matters
The useful control is being able to edit, cancel, or restore content before it publishes, especially when posts represent the contractor business.
What the build does
A controlled path from draft to scheduled post.
The current implementation supports a private operator workflow for composing posts, editing content, setting schedule times, reviewing the queue, and publishing through Greg Staunton's authenticated LinkedIn member authorisation. Every post in this release publishes to Greg Staunton's authenticated personal LinkedIn profile. There is no secondary LinkedIn destination or account selector.
01
Create or enter an approved post
The React interface provides compose and edit screens with validation for content, link URL, image URL, and optional scheduled time.
02
Review the queue
Posts are visible in a queue with status badges, scheduled times, failed-post alerts, cancel controls, and restore for soft-deleted rows.
03
Schedule or publish
The current release supports scheduling posts for later publishing. Immediate publishing can be added when the operator workflow needs it.
04
Prepare the queue
Saving without a date keeps the post as a draft. Choosing a future time moves it into the scheduled queue for cron processing.
05
Publish through Greg's authorisation
The Worker publishes through Greg Staunton's authenticated LinkedIn member authorisation when the post is due and LinkedIn is connected.
06
Record the result
The app records publishing status, LinkedIn post references, failure details, publish events, and scheduler run history where implemented.
Core capabilities
Capabilities shown by the repository today.
Post drafting
Compose, edit, validation, drafts, scheduled state, and link/image fields are implemented in the frontend and API.
Scheduling workflow
Future datetime scheduling, cancel schedule, queue visibility, and a monthly calendar view are implemented.
Publishing controls
Due posts move through scheduled, publishing, posted, and failed states using a two-phase claim to reduce duplicate publishing risk.
LinkedIn OAuth
Admin authorize, callback, status, disconnect, token refresh, and LinkedIn REST publishing service code are present.
Operational visibility
Publish events, scheduler runs, health reporting, failed-post banners, and error handling are implemented.
Image boundary
Image attachments may be stored in the application, but image publishing to LinkedIn is not yet implemented.
Publishing scope
One authorised destination, one private workflow.
Greg Staunton's authenticated personal LinkedIn profile
The current implementation publishes only through Greg Staunton's authenticated personal LinkedIn profile. The content can support greg-staunton.com articles, Oracle Eloqua consulting insight, contractor updates, credentials, certifications, Pl8ypus AI build updates, and Forward Deployed Engineering progress.
Pl8ypus is a content theme and professional brand represented in Greg's posts. It is not a separate LinkedIn publishing identity in this release.
Technical approach
A separate Cloudflare-native application behind the contractor website.
The marketing story lives on pl8ypus.io. The scheduler application stays independent and runs on its own deployment route at the greg-staunton.com tools subdomain pattern.
Frontend
React 19, Vite, React Router, custom Pl8ypus-aligned CSS tokens, compose, posts, calendar, edit, and admin pages.
API layer
Hono routes on Cloudflare Workers expose health, posts, reporting, and LinkedIn admin endpoints.
Storage
Cloudflare D1 stores posts, LinkedIn connection rows, OAuth state, scheduler runs, publish events, and soft-delete state.
Operations
Wrangler deploy, versioned SQL migrations, D1 backup runbook, cron trigger every two minutes, observability enabled, and API rate limiting.
Identity
Cloudflare Access JWT verification protects API routes. Tests bypass this only in the configured test environment.
LinkedIn OAuth is configured with the permissions required for authenticated personal-profile publishing. Authentication, token handling, scheduling, and publishing are performed through the protected application backend.
LinkedIn platform integration
Platform requirements are part of the work.
Personal-profile publishing uses LinkedIn authorisation granted by Greg Staunton as the authenticated member. The application stores the connected member author reference and uses it when publishing approved posts.
The publishing flow is protected behind the private application backend. Token handling, scheduling, publishing attempts, failure handling, and operational history are managed server-side rather than exposed as public website behaviour.
The current implementation does not publish on behalf of other LinkedIn members, manage client accounts, or publish outside Greg Staunton's authenticated personal LinkedIn profile.
Privacy and governance
Narrow purpose, limited data, controlled access.
Minimal collection
The build is designed around post content, scheduling metadata, publish status, and the LinkedIn member connection data needed for Greg's personal publishing workflow.
Purpose-limited use
Data exists to operate LinkedIn content workflow for greg-staunton.com, not for unrelated profiling, advertising resale, or third-party user accounts.
Secret handling
LinkedIn client credentials are expected as Cloudflare Worker secrets. The repository commits only placeholder secret names.
Retention principles
Posts can be soft-deleted and restored, and operational records can support audit, troubleshooting, deletion review, and production accountability.
Development journey
Built in slices, with evidence left behind.
01
Scaffold and data model
Cloudflare Workers, Hono, React/Vite, D1 migrations, health endpoint, and the first posts table.
02
Composition and queue
Compose, edit, delete, list, schedule and cancel actions, with validation and shared form components.
03
Scheduler hardening
Cron publishing, two-phase claims, stuck-publish recovery, status transitions, and tests around idempotency.
04
Observability and recovery
Append-only publish events, scheduler run summaries, failed-post visibility, soft delete, restore, and clearer conflict handling.
05
LinkedIn integration
Admin OAuth routes, callback handling, token storage, token refresh, personal-profile publishing service, and disconnect controls.
06
Deployment readiness
Dedicated custom subdomain route, Cloudflare Access variables, D1 runbook, backup guidance, and production deploy command.
Current boundaries
What this build is not trying to be.
- It is not an enterprise social media management suite.
- It is not a public SaaS product or multi-tenant platform.
- It is not a multi-network publisher.
- It is a private internal application operated by Greg Staunton.
- It does not manage client LinkedIn accounts.
- It publishes only to Greg Staunton's authenticated personal LinkedIn profile.
- It does not publish outside Greg Staunton's authenticated personal LinkedIn profile.
- It does not publish on behalf of other LinkedIn members.
- It does not use LinkedIn data for advertising targeting, behavioural profiling, audience scraping, lead generation, resale, unrelated product development, model training, or runtime AI processing.
Next steps
The remaining work is practical production readiness.
- Complete LinkedIn approval for personal-profile publishing.
- Validate a successful live personal-profile post.
- Strengthen token refresh, reconnection, and failure recovery.
- Improve scheduling controls.
- Improve audit and publishing history.
- Add image publishing only once correctly implemented.
- Improve operational documentation and complete production-readiness testing.
Closing section
A small tool, built all the way to the real edge.
The Social Scheduler AI Build is evidence of focused product delivery: workflow definition, production infrastructure, platform integration, governance, and a clear understanding of where the build still needs approval and hardening.