Project

floopfloop

0.0
The project is in a healthy, maintained state
Build, refine, and manage FloopFloop projects from any Ruby codebase. Stripe-style client with named resources (projects, subdomains, secrets, library, usage, api_keys, uploads, user).
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

floopfloop

Gem Version Gem Downloads CI Ruby version License: MIT

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.message} (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