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

  1. Sign in at partners.ghostswap.io/dashboard.
  2. Submit your application (business name, website, expected monthly volume). A GhostSwap admin will approve.
  3. Once approved (status active), open API Credentials and click Create live credential.
  4. Copy the secret. The dashboard returns a pre-built bearer_token of the form gspk_live_...:gssk_live_... you can drop into your Authorization header. 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.