Docs · Working with Claude Code

The add-integration Skill

Walkthrough of wiring up a new third-party service as an adapter+fake seam.

add-integration wires up a new external service, Slack, OpenAI, S3, any third-party API, using the adapter+fake seam every existing integration in the kit already follows. The concept-level explanation lives at The Adapter+Fake Pattern; this page is the skill's own steps.

The rule this skill enforces

Never call a third-party API directly from a controller or a model. Everything routes through app/adapters/, copying either app/adapters/billing.rb (with its billing/local.rb and billing/stripe.rb) or the simpler app/adapters/bot_check.rb as the template.

The steps

  1. Create app/adapters/<service>.rb as a module with a .for(...), .client, or .enabled? entry point that returns the real client when its credential environment variable is present, and a deterministic fake with canned, sensible values otherwise. Credentials resolve ENV first, then Rails encrypted credentials.
  2. Put the real client and the fake in app/adapters/<service>/. The fake has to be complete enough that the whole feature works offline against it, not just enough to avoid raising an error.
  3. Honor a force-fake override for demos and CI, following the pattern in Billing#stripe?.
  4. Rescue and report failures through ErrorReporter.report(e, context: {...}), and decide deliberately whether this integration should fail open or fail closed; BotCheck#verify is the reference for failing closed.
  5. Document the new credential in Bring Your Own Keys and in .env.example.
  6. If the integration sends the user off-site (a hosted checkout, an OAuth consent screen, a hosted onboarding page), add its host to policy.form_action in config/initializers/content_security_policy.rb and pin it in spec/requests/content_security_policy_spec.rb. This step is the one most often missed, and the failure is completely silent: no exception, no flash, nothing in the network tab, just a button that appears dead. Add data-turbo="false" to any form whose action redirects off-site, on the form or the submit button, or Turbo will submit it via fetch and have the cross-origin redirect blocked by connect-src instead.
  7. Write a spec covering both the fake path (works with zero credentials) and the enabled-but-invalid path (credentials present, service rejects them).
  8. Run bin/check.

Next

See the equivalent walkthroughs for email and background jobs: The add-mailer and add-job Skills.