Documentation

Getting started, authentication, and API reference.

Getting Started
Create an account, create an organization, and generate an API key.

PayKit provides Stellar-based payments, wallets, checkout, swaps, and fiat on/off ramps. To integrate:

  1. Sign in with Google above. You will be redirected back with a JWT used for the developer dashboard.
  2. Create an organization (e.g. "Acme Payments") from the Developer Overview or via POST /organizations.
  3. Create an API key under API Keys. Store the key securely; it is shown only once and cannot be retrieved later.
  4. Use the key in all API requests: x-api-key: pk_xxx or Authorization: Bearer pk_xxx.

Base URL for API requests: NEXT_PUBLIC_API_URL (e.g. https://api.paykit.io). All request/response bodies are JSON unless noted.

Authentication
Dashboard uses OAuth; API calls use API keys. Plan limits apply per organization.

Dashboard (OAuth): Sign in via GET /auth/google; you are redirected to Google and back with a JWT. The JWT is used for developer-only endpoints (organizations, API keys, billing, usage, treasury list).

API (API key): For payments, wallets, checkout, swap, yield, and merchant endpoints, send your API key in every request: header x-api-key: <key> or Authorization: Bearer <key>. The key is tied to an organization; rate limits and usage are applied per organization.

Never expose your API key in client-side code. Use a backend server or proxy to call PayKit with the key.

Payments API
Send payments, create checkout sessions, and manage merchant payouts.

Direct payment: POST /payment — Send from a PayKit wallet to any Stellar address. Body: { "fromWalletId", "toAddress", "asset", "amount" }. Returns transaction hash. Supported assets: XLM, USDC, PYUSD.

Checkout (hosted): POST /merchant/checkout/create — Requires x-api-key. Body: { "amount", "asset", "success_url?", "cancel_url?", "description?", "auto_yield?" }. Returns session id and temporary wallet address. Set auto_yield: true to allocate incoming funds to yield (e.g. 50% liquid, 50% yield strategy).

Merchant payout: POST /merchant/payout — Withdraw from merchant settlement balance. Body: { "destination", "asset", "amount" }. Destination is a Stellar public key.

POST /payment
Body: { "fromWalletId": "...", "toAddress": "G...", "asset": "USDC", "amount": "10" }
→ { "txHash": "..." }

POST /merchant/checkout/create
Headers: x-api-key: <key>
Body: { "amount": "25", "asset": "USDC", "success_url": "https://...", "cancel_url": "https://..." }
→ { "id", "walletAddress", "amount", "asset", "status", "expiresAt" }
Wallet API
Custodial Stellar wallets: create, fetch, and query balances.

POST /wallet/create — Creates a new Stellar wallet (keypair generated and encrypted server-side). Optional body: { "userId" } to associate with a user. Returns id, publicKey, createdAt. New accounts are funded via Friendbot on testnet.

GET /wallet/:id — Returns wallet details. GET /wallet/:id/balance — Returns balances for XLM, USDC, PYUSD (from Horizon).

POST /wallet/create
Body: { "userId?": "..." }
→ { "id", "publicKey", "createdAt" }

GET /wallet/:id/balance
→ { "balances": [{ "asset", "balance", "limit?" }] }
Embedded Wallet
Create wallets by email or social; keys encrypted server-side. Sign transactions via API.

Embedded wallets let your users hold and send crypto without exporting keys. You identify users by email or provider + providerId. One wallet is created per identity; private keys are encrypted and never exposed.

POST /wallet/embedded/create — Body: { "email"? | "provider", "providerId" }. Returns walletId, publicKey, createdAt. Idempotent: same email returns same wallet.

POST /wallet/embedded/sign — Body: { "walletId", "envelopeXdr" } (base64 XDR transaction envelope). Server signs with the wallet key and returns signedEnvelopeXdr. Submit the signed XDR to Horizon yourself or use PayKit payment APIs.

GET /wallet/embedded/balance?walletId=... — Returns balances for XLM, USDC, PYUSD.

const client = createClient({ apiKey: "pk_xxx" });
const { walletId, publicKey } = await client.createEmbeddedWallet({ email: "user@example.com" });
const balances = await client.getEmbeddedBalance(walletId);
Treasury API
Create treasuries, view balances, and enable smart strategies (Earn).

GET /treasury/list — Requires auth. Returns treasuries for the current user. POST /treasury/create — Body: { "name" }. GET /treasury/balance?treasuryAccountId=... — Returns balances and yield info.

Strategies (Earn): GET /treasury/strategies — Returns available strategies: Conservative (80% RWAs, 20% liquidity), Balanced (50% lending, 30% RWAs, 20% liquidity), Yield Max (40% lending, 40% LP, 20% RWAs). Each includes expectedApy and allocation. POST /treasury/strategies/enable — Body: { "treasuryAccountId", "strategy": "conservative" | "balanced" | "yield_max" }.

GET /treasury/strategies → { "strategies": [{ "id", "name", "description", "expectedApy", "allocation" }] }
POST /treasury/strategies/enable
Body: { "treasuryAccountId": "...", "strategy": "balanced" }
→ { "treasuryAccountId", "strategy" }
Swap
On-chain asset swap via Stellar path payments (best price routing). Get a quote before executing.

Swaps use Stellar path payments: you send a fixed amount of one asset and receive the maximum amount of another asset via the best available path. The destination is the same wallet (swap in place). Supported assets: XLM, USDC, PYUSD.

Quote: GET /swap/quote?walletId=...&fromAsset=...&toAsset=...&amount=... — Returns estimated amount out without executing. Response: { "fromAsset", "toAsset", "sendAmount", "estimatedAmountOut", "pathAvailable" }. Use this to show users an estimate before they confirm.

Execute: POST /swap — Body: { "walletId", "fromAsset", "toAsset", "amount" }. Returns txHash and status. fromAsset and toAsset must differ.

GET /swap/quote?walletId=...&fromAsset=USDC&toAsset=XLM&amount=100
→ { "fromAsset": "USDC", "toAsset": "XLM", "sendAmount": "100", "estimatedAmountOut": "12.5", "pathAvailable": true }

POST /swap
Body: { "walletId", "fromAsset": "USDC", "toAsset": "XLM", "amount": "100" }
→ { "txHash", "fromAsset", "toAsset", "amount", "status": "success" }
Liquidity API
Provide liquidity to PayKit payment pools. Earn APR on deposits.

Requires API key (merchant or organization). Deposit USDC (or other assets) into the pool; withdraw anytime. Positions are tracked per organization/merchant.

GET /liquidity/pool — Public. Returns { "totalLiquidity", "apr", "poolId" }.

GET /liquidity/positions — Returns your positions. POST /liquidity/deposit — Body: { "asset?", "amount" }. POST /liquidity/withdraw — Body: { "positionId", "amount?" } (omit amount to withdraw full position).

POST /liquidity/deposit   Body: { "asset": "USDC", "amount": "1000" }
POST /liquidity/withdraw   Body: { "positionId": "...", "amount": "500" }
GET /liquidity/positions   → { "positions": [{ "positionId", "asset", "amount", "poolId", "apr", "createdAt" }] }
Credit API
Stablecoin credit lines — borrow against treasury balance (e.g. 30% of USDC balance).

Credit limit is derived from the treasury's USDC balance. No auth required; pass treasuryAccountId.

GET /credit/limit?treasuryAccountId=... — Returns { "limitAmount", "borrowedAmount", "availableAmount", "currency" }.

POST /credit/borrow — Body: { "treasuryAccountId", "amount" }. Draw from credit line.

POST /credit/repay — Body: { "treasuryAccountId", "amount" }. Repay outstanding balance.

GET /credit/limit?treasuryAccountId=...
→ { "limitAmount", "borrowedAmount", "availableAmount", "currency": "USDC" }

POST /credit/borrow   Body: { "treasuryAccountId", "amount": "5000" }
POST /credit/repay    Body: { "treasuryAccountId", "amount": "1000" }
Anchors (SEP-24)
Stellar anchor platform — interactive deposit and withdraw via anchor's transfer server. Uses SEP-10 JWT when account is a PayKit wallet.

Anchors provide fiat on/off ramps and asset issuance. You request a redirect URL to the anchor's interactive flow; the user completes KYC and transfer on the anchor's site. If account is a PayKit wallet public key, the server performs SEP-10 auth with the anchor and sends the JWT so the request is accepted (avoids 403).

POST /anchor/deposit — Body: { "anchorDomain", "assetCode", "account", "email?", "memo?" }. Returns url (and optional id). Redirect the user to url.

POST /anchor/withdraw — Body: { "anchorDomain", "assetCode", "account", "type?", "email?", "memo?" }. Returns url for the withdraw flow.

anchorDomain is the anchor's domain (e.g. testanchor.stellar.org). account is the Stellar public key that will receive (deposit) or send (withdraw); if it is a PayKit-managed wallet, SEP-10 is used automatically.

POST /anchor/deposit
Body: { "anchorDomain": "testanchor.stellar.org", "assetCode": "USDC", "account": "G...", "email": "user@example.com" }
→ { "url": "https://...", "id?" }
FX API
Cross-border FX routing — get quotes for USD, USDC, XLM pairs.

GET /fx/quote?from=...&to=...&amount=...&walletId? — Returns { "from", "to", "fromAmount", "toAmount", "rate", "route" }. Supported pairs: USD↔USDC, USDC↔XLM, etc. Optional walletId for crypto↔crypto (uses swap quote).

GET /fx/quote?from=USD&to=USDC&amount=100
→ { "from": "USD", "to": "USDC", "fromAmount": "100", "toAmount": "100", "rate": "1", "route": ["USD", "USDC"] }
Onramp / Offramp
Fiat onramp (MoonPay) and offramp (Ramp). Funds arrive in or leave from your PayKit wallet.

Onramp (buy): POST /onramp/buy — Body: { "walletId", "asset?", "fiatAmount?", "fiatCurrency?" }. Returns widgetUrl, sessionId, provider. Open the URL in an iframe or new tab; the user completes KYC and payment with the provider. Crypto is sent to the given walletId.

Offramp (withdraw): POST /onramp/withdraw — Body: { "walletId", "asset?", "amount" }. Initiates a withdrawal to bank/card. Returns withdrawalId, status. Requires sufficient balance in the wallet.

const { widgetUrl } = await client.onrampBuy({ walletId, fiatAmount: 100, fiatCurrency: "USD" });
window.open(widgetUrl);  // or embed in iframe
Yield
Deposit stablecoins to earn yield (Blend Protocol–compatible). Track principal, earnings, and APY.

Yield is earned on assets deposited from a treasury. Supported assets: XLM, USDC (PYUSD if configured).

POST /yield/deposit — Body: { "treasuryAccountId", "asset", "amount" }. Moves funds from the treasury into the yield provider. Returns positionId?, asset, amount.

POST /yield/withdraw — Body: { "treasuryAccountId", "asset", "amount" }. Withdraws from yield back to the treasury.

GET /yield/positions — Query: asset?. Returns positions: array of { "asset", "principal", "yieldEarned", "total", "apy" }.

POST /yield/deposit   Body: { "treasuryAccountId", "asset": "USDC", "amount": "100" }
POST /yield/withdraw  Body: { "treasuryAccountId", "asset": "USDC", "amount": "50" }
GET /yield/positions  → { "positions": [{ "asset", "principal", "yieldEarned", "total", "apy" }] }
Event Streaming
Server-Sent Events (SSE) for real-time payment, wallet, swap, checkout, yield events.

Subscribe to a single stream to receive all supported event types as they occur. No authentication required on the stream URL; events are broadcast to all subscribers.

GET /events/stream — SSE endpoint. Use EventSource in the browser or an SSE client in your backend. Each message is a JSON object: { "id", "type", "data", "createdAt" }.

GET /events/recent?limit=50 — Returns the last N events (for replay or polling). Requires API key.

Event types: payment.created, payment.completed, wallet.created, swap.executed, yield.updated, checkout.completed.

const es = new EventSource("https://api.paykit.io/events/stream");
es.onmessage = (e) => {
  const event = JSON.parse(e.data);
  console.log(event.type, event.data);
};
Webhooks
Register a URL to receive HTTP callbacks for payment, checkout, wallet, and treasury events.

POST /webhooks/register — Body: { "url", "events": ["payment.completed", "checkout.completed", "wallet.created", "treasury.updated", ...], "secret?" }. When an event occurs, PayKit sends a POST to your url with a JSON payload. Optionally provide a secret to verify signatures. Retries are performed on failure.

Your endpoint should respond with 2xx quickly. Process asynchronously if needed. Validate the payload and idempotency (same event id may be delivered more than once).

POST /webhooks/register
Body: { "url": "https://yourapp.com/webhooks", "events": ["payment.completed", "checkout.completed"], "secret": "whsec_..." }
Blockchain Webhooks
Subscribe to blockchain-level events (payment, wallet, swap, checkout) delivered to your URL.

POST /blockchain/webhook — Body: { "url", "event"? | "events"?, "secret?" }. Similar to webhooks above but scoped to blockchain-related events. Events: payment.completed, payment.created, payment.failed, wallet.created, swap.executed, checkout.completed, checkout.failed.

POST /blockchain/webhook
Body: { "url": "https://yourapp.com/blockchain-events", "events": ["payment.completed", "swap.executed"] }
JavaScript SDK
Install paykit-sdk for a typed client. Namespaced API: wallet, treasury, swap, liquidity, credit, yield, fx, payments.

Install: npm install paykit-sdk. Create a client with createClient({ apiKey, baseUrl? }). Use namespaced API: client.treasury, client.swap, client.liquidity, client.credit, client.yield, client.fx, client.payments, client.wallet.

import { createClient } from "paykit-sdk";

const client = createClient({ apiKey: "pk_xxx", baseUrl: "https://api.paykit.io" });

// Treasury (Earn / strategies)
const strategies = await client.treasury.getStrategies();
await client.treasury.enableStrategy({ treasuryAccountId: "...", strategy: "balanced" });
const { balances } = await client.treasury.getBalance(treasuryAccountId);

// Swap
const quote = await client.swap.getQuote({ walletId, fromAsset: "USDC", toAsset: "XLM", amount: "100" });
if (quote.pathAvailable) await client.swap.execute({ walletId, fromAsset: "USDC", toAsset: "XLM", amount: "100" });

// Liquidity
await client.liquidity.deposit({ asset: "USDC", amount: "1000" });
const { positions } = await client.liquidity.positions();

// Credit
const limit = await client.credit.getLimit(treasuryAccountId);
await client.credit.borrow({ treasuryAccountId, amount: "5000" });

// Payments (checkout with auto-yield)
await client.payments.create({ amount: "100", asset: "USDC", autoYield: true });

// FX
const fxQuote = await client.fx.quote({ from: "USD", to: "USDC", amount: "100" });

// Yield
const { positions } = await client.yield.getPositions();
Errors & rate limits
HTTP status codes, error payloads, and plan-based rate limits.

Errors: On failure the API returns an appropriate status (4xx/5xx) and a JSON body with error: a human-readable message. Example: { "error": "Wallet not found" }. Use the message for logging; do not rely on exact strings for logic.

Rate limits: Applied per organization (derived from API key). Limits depend on plan: e.g. Free 10 req/s, Pro 50 req/s, Premium 200 req/s. When exceeded you receive 429; retry after the suggested delay. Monthly API request limits also apply (see Billing).

Idempotency: For payment and checkout creation you can send Idempotency-Key header with a unique value to avoid duplicate charges on retries.