Rails
The FoPost Ruby client wired into Rails, with credentials, ActiveJob jobs, and a webhook engine.
fopost-rails is a thin wrapper over the Ruby SDK. Every request, retry, model, and error class lives there; what this gem adds is Rails wiring: config.fopost and Rails credentials, a memoized thread-safe client, an install generator, ActiveJob jobs so publishing never blocks a request, and a mountable engine that verifies incoming webhooks.
Needs Ruby 3.1 or newer and Rails 7.0 or newer.
bundle add fopost-railsThis is a 0.x release. The surface is still settling and a minor version may break something. Pin an exact version if that matters to you.
Published on RubyGems. Source and issues: github.com/fopost/fopost-rails. MIT licensed.
Configuration
Write the initializer:
bin/rails generate fopost:installCreate an API key at Settings → API Keys in the dashboard and put it somewhere the app can read it:
bin/rails credentials:editfopost:
api_key: fp_your_key_here
default_workspace_id: ws_...
webhook_secret: whsec_...Or in the environment:
FOPOST_API_KEY=fp_your_key_hereEvery setting resolves the same way: what you set explicitly wins, then Rails credentials under fopost:, then the environment, then the default.
| Setting | Credentials key | Environment | Default |
|---|---|---|---|
api_key | fopost: api_key: | FOPOST_API_KEY | none, required |
base_url | base_url | FOPOST_BASE_URL | the hosted API |
timeout | timeout | FOPOST_TIMEOUT | 30.0 |
max_retries | max_retries | FOPOST_MAX_RETRIES | 3 |
default_workspace_id | default_workspace_id | FOPOST_WORKSPACE_ID | none |
webhook_secret | webhook_secret | FOPOST_WEBHOOK_SECRET | none |
queue_name | queue_name | FOPOST_QUEUE | default |
Set them in the initializer:
Fopost::Rails.configure do |config|
config.api_key = Rails.application.credentials.dig(:fopost, :api_key)
config.queue_name = 'social'
endor from config/application.rb:
config.fopost.default_workspace_id = 'ws_...'Quick start
Fopost::Rails.client is a configured Fopost::Client, memoized and safe to call from any thread. The Ruby SDK page documents the full resource surface.
class PostsController < ApplicationController
def index
@posts = Fopost::Rails.client.posts.list(status: 'scheduled')
end
def create
post = Fopost::Rails.client.posts.create(
workspace_id: Fopost::Rails.config.default_workspace_id,
content: params.require(:text),
accounts: params.require(:account_ids)
)
Fopost::Rails::PublishJob.perform_later(post.id)
redirect_to posts_path, notice: 'Queued for publishing.'
end
endErrors are the SDK's, so one rescue_from covers the lot:
rescue_from Fopost::PaymentRequiredError do |error|
redirect_to error.upgrade_url, alert: error.message
end
rescue_from Fopost::Error do |error|
Rails.logger.error("FoPost: #{error}")
head :bad_gateway
endBackground jobs
Publishing reaches a third-party network, so it belongs off the request cycle.
# Publish something that already exists.
Fopost::Rails::PublishJob.perform_later(post.id)
# Compose and, optionally, send in one job.
Fopost::Rails::CreatePostJob.perform_later(
content: 'Shipping today.',
accounts: account_ids,
publish: true
)
# Or schedule it, and pass anything else the SDK takes through `options`.
Fopost::Rails::CreatePostJob.perform_later(
workspace_id: 'ws_...',
content: ['First post in the thread', 'And the reply'],
accounts: account_ids,
status: 'scheduled',
schedule_at: 1.hour.from_now,
options: { labels: ['launch'], title: 'Launch week' }
)workspace_id falls back to config.default_workspace_id. Both jobs run on config.queue_name.
When the API answers 429, the job is re-enqueued for exactly the interval the API asked for in Retry-After, capped at a minute, up to five attempts. Every other Fopost::Error is left to your queue's own error handling.
Publishing returns once delivery is queued, not once it is live. Subscribe to fopost.post.published for that.
Receiving webhooks
Mount the engine:
# config/routes.rb
mount Fopost::Rails::Engine => '/fopost'That serves POST /fopost/webhooks. Create a webhook pointing at it, copy the secret it shows you once into config.webhook_secret, and subscribe:
# config/initializers/fopost_webhooks.rb
ActiveSupport::Notifications.subscribe('fopost.post.published') do |*, payload|
payload[:event] # "post.published"
payload[:data] # the event body FoPost sent
payload[:timestamp] # ISO 8601, when FoPost sent it
payload[:delivery_id] # X-FoPost-Delivery, unique per attempt
payload[:payload] # the whole parsed body
endTwo notifications fire per verified delivery: fopost.<event>, and fopost.webhook as a catch-all. The events are post.published, post.failed, post.partially_failed, delivery.published, delivery.failed, delivery.delayed, and account.health_changed.
Verification is not optional and not yours to write. FoPost signs the exact bytes of the request body with HMAC-SHA256, keyed by the webhook secret, and sends the hex digest as X-FoPost-Signature: sha256=<digest>. The controller recomputes it over the raw body and compares in constant time; a mismatch is a 401 and publishes nothing, and an unconfigured secret is a 503 rather than a pretended success.
To sign a request yourself, in a request spec:
body = { event: 'post.published', data: { postId: 'post_1' } }.to_json
post '/fopost/webhooks',
params: body,
headers: {
'CONTENT_TYPE' => 'application/json',
'X-FoPost-Signature' => Fopost::Rails::WebhookSignature.sign(body, secret)
}Testing your app
Swap the client for one wired to your own transport and nothing touches the network:
Fopost::Rails.client = Fopost::Client.new(api_key: 'fp_test', transport: my_stub)Fopost::Rails.reset! puts config and client back to their defaults between tests.
Next
Related documentation
- SDKs & Integrations
Official FoPost clients for TypeScript, Python, PHP, Ruby, Go, Rust, Java, .NET, Swift, Kotlin, Dart, and Elixir, framework integrations from Laravel to Next.js, and tooling for the CLI, CI, Terraform, and the automation platforms.
- SDKs Overview
Every official FoPost client, what it covers, and how to pick one.
- TypeScript SDK
The official TypeScript and Node.js client for the FoPost API.
- Python SDK
The official Python client for the FoPost API.
- PHP SDK
The official PHP client for the FoPost API, with no framework and no HTTP library.
- Ruby SDK
The official Ruby client for the FoPost API, with no runtime dependencies.
- Go SDK
The official Go client for the FoPost API.
- Rust SDK
The official Rust client for the FoPost API, async and built on reqwest.