floopfloop
Official Ruby SDK for the FloopFloop API. Build, refine, and manage FloopFloop projects from any Ruby codebase (Ruby ≥ 3.0). Zero runtime dependencies — stdlib net/http + json.
Install
gem install floopfloop
Or in your Gemfile:
gem "floopfloop", "~> 0.1.0.alpha.1"
Quickstart
Grab an API key: floop keys create my-sdk (via the floop CLI) or the dashboard → Account → API Keys. Business plan required to mint new keys.
require "floopfloop"
client = FloopFloop::Client.new(api_key: ENV.fetch("FLOOP_API_KEY"))
# Create a project and wait for it to go live.
created = client.projects.create(
prompt: "A landing page for a cat cafe with a sign-up form",
name: "Cat Cafe",
subdomain: "cat-cafe",
bot_type: "site",
)
live = client.projects.wait_for_live(created.fetch("project").fetch("id"))
puts "Live at: #{live['url']}"
Streaming progress
client.projects.stream(project_id) do |ev|
printf("%s (%d/%d) — %s\n", ev["status"], ev["step"], ev["totalSteps"], ev["message"])
end
The block is called for every unique status snapshot; duplicates (same status / step / progress / queuePosition) are filtered out so you don't see dozens of identical "queued" events. On success returns the final live event; raises FloopFloop::Error on BUILD_FAILED / BUILD_CANCELLED / TIMEOUT.
If you just want to block until live, wait_for_live wraps stream and fetches the hydrated project hash:
live = client.projects.wait_for_live(project_id)
Error handling
Every call raises FloopFloop::Error on non-2xx. Inspect #code:
begin
client.projects.status("my-project")
rescue FloopFloop::Error => e
case e.code
when "RATE_LIMITED" then sleep(e.retry_after || 5); retry
when "UNAUTHORIZED" then abort "Check your FLOOP_API_KEY."
else
warn "[#{e.code} #{e.status}] #{e.} (request #{e.request_id})"
raise
end
end
Known codes: UNAUTHORIZED, FORBIDDEN, VALIDATION_ERROR, RATE_LIMITED, NOT_FOUND, CONFLICT, SERVICE_UNAVAILABLE, SERVER_ERROR, NETWORK_ERROR, TIMEOUT, BUILD_FAILED, BUILD_CANCELLED, UNKNOWN. Unknown server codes pass through verbatim in #code.
Resources
| Accessor | Methods |
|---|---|
client.projects |
create, list, get, status, cancel, reactivate, refine, conversations, stream, wait_for_live |
client.subdomains |
check, suggest |
client.secrets |
list, set, remove |
client.library |
list, clone |
client.usage |
summary |
client.api_keys |
list, create, remove (accepts id or name) |
client.uploads |
create(file_name:, bytes:, file_type: nil), create_from_path(path, file_type: nil) |
client.user |
me |
Method-for-method parity with @floopfloop/sdk (Node), floopfloop (Python), floopfloop (Rust), and floop-go-sdk (Go).
For longer end-to-end patterns — streaming a build, refining mid-deploy, attachment uploads, key rotation, retry-with-backoff — see the cookbook.
Uploading attachments
# From bytes
att = client.uploads.create(
file_name: "screenshot.png",
bytes: File.binread("./screenshot.png"),
)
# Or the one-shot helper — reads the file for you
att = client.uploads.create_from_path("./screenshot.png")
# Drop into a refine call:
client.projects.refine(project_id,
message: "Redo the landing page based on this screenshot.",
attachments: [att],
)
Max 5 MB per file; allowed: png, jpg, gif, svg, webp, ico, pdf, txt, csv, doc, docx.
Configuration
client = FloopFloop::Client.new(
api_key: ENV.fetch("FLOOP_API_KEY"),
base_url: "https://staging.floopfloop.com", # default production URL
timeout: 60, # default 30s
user_agent_suffix: "myapp/1.2", # appended after floopfloop-ruby-sdk/<v>
)
FloopFloop::Client is thread-safe — each request opens its own Net::HTTP session. Reuse a single client across threads / workers without contention.
Versioning
Follows Semantic Versioning. Breaking changes in 0.x are called out in CHANGELOG.md and a new tag is cut with v<version>. Tag push triggers the release workflow which publishes to RubyGems.
License
MIT