| name | integrate-ramp-provider |
| description | Start here to add a new on/off-ramp provider (fiat↔crypto) to SDP. Routes you through the integration sequence and the non-negotiable rules; the individual steps live in their own skills. Use when opening a PR against apps/sdp-api to integrate a ramp or payment provider. |
Integrate a ramp provider
The umbrella. This ties the per-capability skills into one sequence. Read apps/sdp-api/src/lib/ramps/providers/lightspark/client.ts as the canonical example, then work the steps below.
Inputs
docs — your provider's API documentation URL (e.g. docs: https://docs.yourprovider.com). Pass it when you start; every step uses it as the source of truth for your endpoints, auth, and payload shapes when mapping your API onto SDP's contract. The closer your docs, the less guesswork.
Enabling Payments v2
Ramps live in Payments v2, which is feature-flagged off by default — so set the override cookie in the browser to see the v2 dashboard and exercise your integration through the UI:
sdp_dashboard_payments_v2_override=enabled
(Constant DASHBOARD_PAYMENTS_V2_OVERRIDE_COOKIE_NAME in apps/sdp-web/src/lib/dashboard-feature-flags.ts; read server-side, default off. Set it yourself in the browser — disabled or clearing the cookie reverts to legacy payments.)
Sequence
Do them in this order; skip the flows you don't support.
- register-provider — scaffold: add the id to
RAMP_PROVIDERS, register the client, fill the dispatch switches, write the mode-keyed config reader + declare its env vars. Make it compile.
- rail-discovery — declare which fiat/crypto rails you support.
- integrate-estimate — rate preview; the cheapest live end-to-end check (no DB, no KYC).
- counterparty-requirements — KYC gating for the flows.
- integrate-onramp / integrate-offramp — the quote flow(s) for the direction(s) you support.
- integrate-webhook — settlement events and reconciliation.
The type system is the checklist: adding the id in step 1 breaks compilation at every site a provider must be wired (the as const satisfies Record<RampProviderId, …> registry + the exhaustive switch/never defaults). Fix each — don't add a fallback to silence it.
Rules that aren't optional (shared by every step)
- No fallbacks. No
?? default, || [], swallowed try/catch, or "shouldn't happen" guards. Required data is required — throw and fail loud.
- HTTP in the provider; DB in the route handler. Providers read creds from the passed
env keyed by mode and never touch the database.
- Secrets are environment variables, mode-keyed (
<KEY> and <KEY>_SANDBOX); a missing one throws providerNotConfigured → HTTP 503. (This repo injects them via Doppler in deploy, but your code just reads env[...] — supply them however your environment does.)
- Webhooks are fully typed — parse the raw body as
unknown only at the signature boundary, then narrow.
- Strong typing — no
any, no enum, finite sets are as const satisfies Record<…>.
- Verify with
tsc --noEmit + biome check (ESLint is broken repo-wide — don't use it).
Per-step detail lives in each linked skill.