Swap provider

Set up a swap provider

Allow purchasers to pay with USDT, USDC, ETH, or SOL. The swap provider securely converts the amount to Bitcoin Lightning.

Create API credentials with a recommended provider below, or build your own, then set the resulting Lightning Swap Connect URI as LSC_URI_PRIMARY in your server environment.

Recommended

Lightning Swap #

Lightning Swap is a lightning-native swap service. Create one API key in the dashboard, then copy the Lightning Swap Connect URI into your server environment as LSC_URI_PRIMARY.

Lightning Swap homepage with Sign in for API keys highlighted
Open lightning-swap.com and choose Sign in for API keys.
Lightning Swap API keys page with Create key form
Enter a label, then tap Create key.
Lightning Swap API key detail showing the Lightning Swap Connect URI and Copy button
Tap Copy to copy the Lightning Swap Connect URI.
  1. Go to lightning-swap.com and tap Sign in for API keys.
  2. Sign in, then open the API keys page.
  3. Enter a label and tap Create key.
  4. Tap Copy to copy the Lightning Swap Connect URI. Store the whole URI as one secret — it carries both the key and the secret.
  5. Set it on your server as the LSC_URI_PRIMARY environment variable (use LSC_URI_BACKUP for a backup provider, which is consulted only when the primary is unreachable).

.env (or however you add environment variables)

LSC_URI_PRIMARY=lightning+swapconnect://lightning-swap.com/?key=…&secret=…
Recommended

FixedFloat #

Create one API key in Account settings, then fold the key and secret into a Lightning Swap Connect URI and set it as LSC_URI_PRIMARY.

FixedFloat Account menu with API management highlighted
Open the Account menu and choose API management.
FixedFloat Generate API Key page with Generate button highlighted
Tap Generate to create your API key.
  1. Go to ff.io and sign in.
  2. Open Account, then choose API management.
  3. Tap Generate. FixedFloat allows one API key per account.
  4. Copy the API key and the API secret.
  5. Drop both into the Lightning Swap Connect URI below, keeping ff.io as the host.
  6. Set the finished URI on your server as LSC_URI_PRIMARY (or LSC_URI_BACKUP, which is consulted only when the primary is unreachable).

Build your Lightning Swap Connect URI

One URI carries the host, the key, and the secret. Replace the two highlighted placeholders with your FixedFloat values — everything else stays exactly as shown.

LSC_URI_PRIMARY=lightning+swapconnect://ff.io/?key=YOUR-KEY-HERE&secret=YOUR-SECRET-HERE
  • ff.io FixedFloat’s API host — leave it as it is.
  • YOUR-KEY-HERE Your FixedFloat API key.
  • YOUR-SECRET-HERE Your FixedFloat API secret.

Store the finished URI as one secret and keep it on the server — it is a bearer credential for your FixedFloat key.

For teams

Build your own #

Build your own altcoin → Lightning swap provider by conforming to the Lightning Swap API, which is a subset of the FixedFloat API.

Point OpenReceive at your service the same way you would Lightning Swap or FixedFloat: construct your own Lightning Swap Connect URI — your host, your API key, your API secret — and set it as LSC_URI_PRIMARY.

LSC_URI_PRIMARY=lightning+swapconnect://your-swap-host.example/?key=YOUR-KEY-HERE&secret=YOUR-SECRET-HERE

A protocol proven in production #

The Lightning Swap API keeps FixedFloat's paths, HMAC-signed requests, response envelope, currency codes and order object, and leaves out everything a swap into Lightning does not need. FixedFloat has run this protocol in production for years, so the hard cases already have answers: a deposit that arrives short, one that arrives too large, and one that arrives after the quote expired.

Why refunds are part of the protocol #

With stablecoins, refunds are unfortunately necessary. Payers send USDT and USDC from on-chain wallets and exchanges, where withdrawal fees get deducted, amounts get rounded, and transfers land after the deposit window closed. A Lightning payment can be exact; these cannot. And a mismatched deposit cannot simply go back where it came from, because exchange withdrawals leave from shared wallets.

FixedFloat's refund protocol handles this in four explicit steps:

  1. The order moves to EMERGENCY and says why: LESS (underpaid), MORE (overpaid), EXPIRED (arrived late) or LIMIT (outside the limits).
  2. Nothing moves on its own: the merchant's Lightning invoice is not paid and no refund is sent.
  3. The payer gives a refund address on the same network, and OpenReceive calls /emergency with choice: "REFUND".
  4. Your service sends the deposit back, records the transaction on the order as back.tx, and the order ends DONE.

It is a small state machine with no guesswork, and it really works. It is also understandable: if you know what you are doing, you can implement it.

Endpoints OpenReceive calls #

The host comes from your Lightning Swap Connect URI; authenticated methods live under /api/v2.

  • GET /rates/fixed.xml Required

    Public, with no key. OpenReceive caches it to show each coin's indicative amount, minimum and maximum before an order exists. List a BTCLN pair for every coin you accept.

  • POST /api/v2/ccies Required

    Your currency catalog. OpenReceive maps USDT, USDC, SOL and ETH to your codes and finds the Lightning payout code (BTCLN, with send: 1).

  • POST /api/v2/create Required

    The binding order. OpenReceive sends type: "fixed", direction: "to", toCcy: "BTCLN", the amount in BTC, and toAddress: a BOLT11 invoice from the merchant's own wallet. Return id, token, status, time.expiration, and the deposit from.address and from.amount, plus from.tag where the network needs a memo.

  • POST /api/v2/order Required

    Polled with id and token until the order is DONE, EXPIRED or EMERGENCY.

  • POST /api/v2/emergency Required

    Sent with choice: "REFUND" and the payer's refund address (and tag), after OpenReceive refreshes the order.

  • POST /api/v2/price Recommended

    Called after create to show the payer the swap fee. If it fails, the fee is simply not shown.

You can skip the rest of the FixedFloat API: float rates, on-chain payouts, direction: "from" orders, the EXCHANGE emergency choice, and the email and QR helpers.

Rules for your service #

  • Signed requests. Every POST carries X-API-KEY and X-API-SIGN, the lowercase hex HMAC-SHA256 of the exact request body under the API secret. An empty body is {}, signed as such.
  • One envelope. Every response is { "code": 0, "msg": "OK", "data": … }; a non-zero code is an error.
  • FixedFloat's statuses. NEW, PENDING, EXCHANGE, WITHDRAW, then DONE; or EXPIRED; or EMERGENCY. Spell them exactly.
  • The order token is a secret. /order and /emergency require both the order id and its token.
  • Pay each invoice at most once, and only after the deposit has the confirmations you require. A Lightning payment cannot be pulled back.
  • Your status never marks an order paid. OpenReceive marks an order paid only when the merchant's own wallet reports the Lightning invoice settled. DONE without a settled invoice does not count.
Read Lightning Swap API docs FixedFloat API reference