Quickstart
You'll go from "no credential" to "swap created" in five steps. ~15 minutes. Server-side only — never put credentials in browser code.
⚡ Fastest path: have an AI write it for you
Click the ⌘ Copy for LLMs button (top right of this page) — it copies a 20 KB self-contained brief to your clipboard. Paste it into Claude or ChatGPT with a prompt like:
Build me a Node.js Express server that integrates this API end-to-end. The user picks two currencies, sees a quote, enters payout/refund addresses, confirms, and watches the status update until terminal. Use the reference implementation in the brief as a starting point.
The brief includes a complete working package.json, server.js, public/index.html, and public/app.js — the AI just adapts it to your project. ~30 seconds from copy to working code.
If you'd rather build it by hand, continue below.
1. Get a credential
- Sign in at partners.ghostswap.io/dashboard.
- Submit your application (business name, website, expected monthly volume). A GhostSwap admin will approve.
- Once approved (status
active), open API Credentials and click Create live credential. - Copy the secret. The dashboard returns a pre-built
bearer_tokenof the formgspk_live_...:gssk_live_...you can drop into yourAuthorizationheader. If you lose the plaintext later, you can re-view it via the Reveal secret button on the same page (audit-logged on our side).
2. Make your first call
const BASE = 'https://partners-api.ghostswap.io';
const AUTH = `Bearer ${process.env.GHOSTSWAP_PUBLIC_KEY}:${process.env.GHOSTSWAP_SECRET}`;
const res = await fetch(`${BASE}/v1/currencies?lite=true`, {
headers: { 'Authorization': AUTH },
});
const { currencies } = await res.json();
console.log(currencies); // ["btc", "eth", "ltc", ...]cURL equivalent:
curl https://partners-api.ghostswap.io/v1/currencies?lite=true \
-H "Authorization: Bearer gspk_live_...:gssk_live_..."3. Get a quote
const res = await fetch(`${BASE}/v1/quotes`, {
method: 'POST',
headers: { 'Authorization': AUTH, 'Content-Type': 'application/json' },
body: JSON.stringify({ from: 'btc', to: 'eth', amountFrom: '0.01' }),
});
const { quote } = await res.json();
// quote.amountUserReceives is the headline number to display.4. Create a swap (idempotent)
import { randomUUID } from 'node:crypto';
const res = await fetch(`${BASE}/v1/swaps`, {
method: 'POST',
headers: {
'Authorization': AUTH,
'Content-Type': 'application/json',
'Idempotency-Key': randomUUID(),
},
body: JSON.stringify({
from: 'btc',
to: 'eth',
amountFrom: '0.01',
address: '0xUserPayoutAddress...',
// refundAddress is optional. Include it if you have one on the `from`
// chain; omit it otherwise — your end-user can still receive funds.
refundAddress: 'bc1qUserRefundAddress...',
partnerReferenceId: 'order_42',
}),
});
const { swap } = await res.json();
// Display swap.payinAddress to your user.5. Poll for status
const TERMINAL = new Set(['finished', 'failed', 'refunded', 'overdue', 'expired']);
async function poll(id) {
while (true) {
const r = await fetch(`${BASE}/v1/swaps/${id}`, { headers: { 'Authorization': AUTH } });
const { swap } = await r.json();
console.log(`Status: ${swap.status}`);
if (TERMINAL.has(swap.status)) return swap;
await new Promise((res) => setTimeout(res, 30_000));
}
}When swap.status === 'finished', your user has been credited at their payoutAddress.
What's next
- End-to-end guide — full reference walkthrough with error handling.
- Status lifecycle — every state transition and what each means.
- Idempotency — required rules, deduplication semantics.
- Errors — error envelope and how to recover from each type.