Any media_urls (or media_ids) entry can also be a Hash carrying per-media
alt text (max 1500 chars), delivered to Mastodon, Bluesky, X (photos/GIFs), and
Pinterest:
Code
media_urls: [{ "url" => "https://example.com/teaser.jpg", "alt" => "Red sneaker on a white background" }]
Publish immediately:
Code
post = client.posts.create_and_publish( content: "We are live!", channels: ["x", "bluesky"])
Local files: client.media.upload(file: "/path/to/image.jpg", name: "hero-shot")
(file accepts a path String/Pathname, an IO, or a raw binary String). Base64:
client.media.upload_from_base64(data: b64_string, mime_type: "image/png").
Analytics
Code
# One poststats = client.analytics.post("123")# Up to 100 posts in one request (avoids rate limit trouble when syncing)bulk = client.analytics.posts(["123", "124", "125"])# Workspace overviewoverview = client.analytics.overview(period: "30d")# Account-level stats (followers etc.)accounts = client.analytics.accounts(platform: "instagram")
Verify webhooks
Rails controller:
Code
class OmnisocialsWebhooksController < ApplicationController skip_before_action :verify_authenticity_token def create payload = request.body.read # raw body, exactly as received signature = request.headers["X-OmniSocials-Signature"] begin event = OmniSocials::Webhooks.verify( payload: payload, signature: signature, secret: ENV.fetch("OMNISOCIALS_WEBHOOK_SECRET"), tolerance: 300 ) rescue OmniSocials::WebhookVerificationError return head :bad_request end if event["type"] == "post.published" event["data"]["targets"].each do |target| Rails.logger.info "#{target["platform"]} #{target["status"]} #{target["native_post_id"]}" end end head :ok endend
Errors
The base class is OmniSocials::Error. OmniSocials::APIError covers HTTP errors
with subclasses AuthenticationError (401), PermissionDeniedError (403),
NotFoundError (404), ValidationError (400/422), RateLimitError (429, with
#retry_after), and ServerError (5xx), plus APIConnectionError and
WebhookVerificationError. Each carries status, code, message, and body.
Code
begin client.posts.get("does-not-exist")rescue OmniSocials::NotFoundError => e puts "Gone: #{e.code} #{e.message}"rescue OmniSocials::RateLimitError => e puts "Slow down, retry in #{e.retry_after}s"rescue OmniSocials::ValidationError => e puts "Bad request: #{e.body}"rescue OmniSocials::AuthenticationError puts "Check your API key"rescue OmniSocials::APIConnectionError puts "Network problem"rescue OmniSocials::APIError => e puts "API error: #{e.status} #{e.message}"end
Configuration
Code
client = OmniSocials::Client.new( api_key: "omsk_live_...", timeout: 60, # seconds max_retries: 5, # retries after the first attempt base_url: "https://api.omnisocials.com/v1" # override for testing)
Notes
A fresh connection is used per request; there is nothing to close
All responses are plain Hashes with string keys
Omitting folder_id/parent_id leaves a value unchanged; passing nil explicitly moves to root/top level