Documentation

Clink SDK Documentation

Last updated April 8, 2026

This guide is focused on developers integrating clink-sdk. It keeps the docs centered on what your customers need to ship payments, and leaves out internal operator details that only Clink needs to manage behind the scenes.

Get an API key

Before you can start testing the SDK, request your API key from Clink.

Apply for an API key

Installation

bash
npm install clink-sdk

Requirements: Node.js >= 24

Authentication

All SDK requests require your API key.

Pass it as secretKey in the constructor.

SDK quickstart

ts
import Clink from 'clink-sdk';

const clink = new Clink({
  secretKey: process.env.CLINK_SECRET_KEY,
  webhookSecret: process.env.CLINK_WEBHOOK_SECRET,
});

// Create a payment
const payment = await clink.payments.create({
  amount: 10,
  currency: 'USDC',
  localCurrency: 'NGN',
  callbackUrl: 'https://yourapp.com/webhooks/clink',
  customerEmail: 'buyer@example.com',
  metadata: { orderId: 'order_123' },
});

// Show payment.stellarAddress and payment.memo to the customer

// Verify after the customer sends USDC
const updated = await clink.payments.verify(payment.id);
console.log(updated.status); // 'settled'

SDK configuration

OptionTypeRequiredDefaultDescription
secretKeystringYes-Your Clink API key
webhookSecretstringNosecretKeySecret used to verify webhook payloads
environment'testnet' | 'mainnet'No-Select the Clink environment to work against

Payments

Use the payments API to create payment instructions, verify incoming transfers, and inspect payment history from your app.

Create a payment

Creates a new pending payment and returns the Stellar address and memo to show your customer.

SDK:

ts
const payment = await clink.payments.create({
  amount: 10,
  currency: 'USDC',
  localCurrency: 'NGN',
  callbackUrl: 'https://yourapp.com/webhooks/clink',
  description: 'Pro plan subscription',
  customerEmail: 'buyer@example.com',
  metadata: { orderId: 'order_123' },
});

Parameters:

FieldTypeRequiredDescription
amountnumberYesAmount in USDC
currency'USDC'YesMust be 'USDC'
localCurrencystringYesPayout currency - NGN, GHS, KES, or UGX
callbackUrlstringYesHTTPS URL Clink will POST webhook events to
descriptionstringNoPayment description
customerEmailstringNoCustomer email address
metadataobjectNoArbitrary key-value data returned in webhooks

Response: Payment object

Verify a payment

Checks Stellar for an incoming USDC transaction matching the payment. If found, triggers settlement and returns the updated payment.

ts
const payment = await clink.payments.verify('pay_abc123');

Response: Payment object

List payments

ts
// All payments
const payments = await clink.payments.list();

// With filters
const payments = await clink.payments.list({
  status: 'pending',
  limit: 20,
});

Query parameters:

ParameterTypeDescription
statusstringFilter by status: pending, confirmed, settled, expired, failed
limitnumberMax results to return (default: 20)

Response: Array of Payment objects

Payment object

json
{
  "id": "pay_a1b2c3d4e5f6g7h8i9",
  "stellarAddress": "GCEUHLTXIODT3XXIKZZKHZWX5A2H54BGKGKZPWRZZEZBHOK26C7OEEWR",
  "memo": "clink-pay_a1b2c3d4e5f6g7h8i9",
  "amount": 10,
  "currency": "USDC",
  "localCurrency": "NGN",
  "localAmount": 16000,
  "status": "settled",
  "description": "Pro plan subscription",
  "customerEmail": "buyer@example.com",
  "stellarTxHash": "abc123...",
  "callbackUrl": "https://yourapp.com/webhooks/clink",
  "metadata": { "orderId": "order_123" },
  "expiresAt": "2025-01-01T00:30:00.000Z",
  "createdAt": "2025-01-01T00:00:00.000Z",
  "settledAt": "2025-01-01T00:05:00.000Z"
}

Fields:

FieldTypeDescription
idstringUnique payment ID
stellarAddressstringStellar address the customer sends USDC to
memostringMemo the customer must include with their transaction
amountnumberUSDC amount
currencystringAlways USDC
localCurrencystringPayout currency
localAmountnumberSettled local currency amount (present after settlement)
statusstringCurrent payment status
stellarTxHashstringStellar transaction hash (present after confirmation)
metadataobjectYour custom metadata
expiresAtstringISO 8601 expiry time
createdAtstringISO 8601 creation time
settledAtstringISO 8601 settlement time (present after settlement)

Important: Always instruct your customer to include the memo exactly when sending USDC. Without the memo, Clink cannot match the transaction to the payment.

Payment lifecycle

flow
pending --> confirmed --> settled
   |
   +--> expired
   |
   +--> failed
StatusDescription
pendingPayment created, waiting for USDC on Stellar
confirmedUSDC received on Stellar, settlement in progress
settledLocal currency payout complete
expiredUSDC not received before the payment expires
failedSettlement failed after USDC was received

Webhooks

Clink sends a signed POST request to your callbackUrl whenever a payment status changes.

Webhook events

EventFired when
payment.confirmedUSDC arrives on Stellar
payment.settledLocal currency payout is complete
payment.failedSettlement fails
payment.expiredPayment expires without receiving USDC

Webhook payload

json
{
  "event": "payment.settled",
  "data": {
    "id": "pay_a1b2c3d4e5f6g7h8i9",
    "status": "settled",
    "localAmount": 16000,
    "metadata": { "orderId": "order_123" }
  },
  "signature": "a3f9c1e2b4d8..."
}

The signature is also sent as the x-clink-signature request header.

Verifying signatures

Always verify the signature before processing a webhook. Ignore any webhook with an invalid signature.

ts
app.post('/webhooks/clink', express.json(), async (req, res) => {
  const valid = clink.webhooks.verify({
    payload: req.body,
    signature: req.headers['x-clink-signature'] as string,
    secret: process.env.CLINK_WEBHOOK_SECRET,
  });

  if (!valid) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const { event, data } = req.body;

  switch (event) {
    case 'payment.settled':
      await fulfillOrder(data.metadata.orderId);
      break;
    case 'payment.expired':
      break;
    case 'payment.failed':
      break;
  }

  res.json({ received: true });
});

Webhook response: Always return 200 quickly. Clink retries failed deliveries up to 3 times with exponential backoff.

Error handling

All SDK errors follow this shape:

json
{
  "error": "ERROR_CODE",
  "message": "Human-readable description",
  "details": {}
}

Common codes:

CodeHTTP statusDescription
INVALID_API_KEY401Missing or invalid API key
INVALID_PAYMENT_REQUEST400Invalid payment parameters or duplicate email
INVALID_CONFIGURATION400Invalid SDK configuration
PAYMENT_NOT_FOUND404Payment ID does not exist
PAYMENT_EXPIRED410Payment has expired
INVALID_SIGNATURE401Webhook signature verification failed

SDK error handling:

ts
import Clink, { ClinkError } from 'clink-sdk';

try {
  const payment = await clink.payments.create({ ... });
} catch (error) {
  if (error instanceof ClinkError) {
    console.error(error.code);
    console.error(error.message);
    console.error(error.details);
  }
}

Supported currencies

CodeCurrencyCountry
NGNNigerian NairaNigeria
GHSGhanaian CediGhana
KESKenyan ShillingKenya
UGXUgandan ShillingUganda

Environment variables

VariableDescription
CLINK_SECRET_KEYYour Clink API key
CLINK_WEBHOOK_SECRETSecret for verifying webhook payloads

The SDK can read these values from your environment so you do not have to hardcode them in your application.