API Reference
106 endpoints

API Reference

Every HTTP endpoint CreativMoney exposes, plus the guides you need to integrate confidently. Click any path to copy it. Press / to search.

Base URL
Public13User65Admin22Webhook4Cron2Rate-limited32PIN-gated5

Guides

Getting started

Create an account, log in, make your first call.

CreativMoney is a P2P crypto trading platform for African markets. The HTTP API mirrors everything the web and mobile apps do — list wallets, send funds, open trades, approve settlements, reconcile ledgers. Most endpoints live behind the same session cookie the browser uses; a few use HMAC-signed webhooks or a shared cron secret.

Three minutes end-to-end:

  • Sign up for an account at /register (or POST /api/auth/register).
  • Verify your email with the 6-digit OTP sent to you.
  • Log in via the web app or POST the credentials to the NextAuth endpoint. The response sets an authjs.session-token cookie — every subsequent authenticated request sends it automatically if you stay on the same origin.
  • Call GET /api/wallets to list your wallets. That's a canonical first authenticated request.
Your first authenticated call (replace COOKIE with the session cookie from the browser devtools)
curl https://www.creativmoney.com/api/wallets \
  -H "Cookie: authjs.session-token=COOKIE"

Everything under /api/* that isn't in the public allowlist returns 401 for anonymous callers. The error body is always `{ "message": "Unauthorized" }`.

Authentication

Five auth classes, one per endpoint colour.

Every endpoint lists its auth class in the badge column (Public, User, Admin, HMAC, Cron). The reference below shows how to satisfy each one.

Public
Anyone can call. No credentials. Useful for the landing page feeds and this documentation itself.
User
Any authenticated user. Send the session cookie from login.
Admin
Same cookie — but the caller's user row must have role ADMIN or SUPER_ADMIN. Otherwise 403.
HMAC
Webhook bodies signed with HMAC-SHA256. The signature goes in an x-*-signature header; the secret is shared out-of-band.
Cron
Internal invocations authenticated via Authorization: Bearer ${CRON_SECRET}. Do not expose this secret.
User (session cookie) — most endpoints
curl https://www.creativmoney.com/api/user/activities?limit=20 \
  -H "Cookie: authjs.session-token=eyJhbGciOi..."
HMAC (webhook) — signature-verified body
BODY='{"event":"payment.received","hash":"..."}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$IBEX_WEBHOOK_SECRET" -r | cut -d' ' -f1)

curl https://www.creativmoney.com/api/webhooks/ibex \
  -X POST \
  -H "Content-Type: application/json" \
  -H "x-ibex-signature: $SIG" \
  -d "$BODY"
Cron — internal scheduler
curl https://www.creativmoney.com/api/cron/expire-pending-txs \
  -H "Authorization: Bearer $CRON_SECRET"

PIN & money-moving calls

Second factor for anything that debits a wallet.

Endpoints that move money require a 4-digit PIN in the request body on top of the session cookie. The PIN is the second factor — a stolen cookie alone cannot drain a wallet. Five wrong PIN attempts lock the account for 15 minutes (bcrypt + lockout via lib/pin.ts).

PIN-gated endpoints are marked with a 🔒 PIN badge in the reference below. Currently:

  • POST /api/wallets/send
  • POST /api/wallets/[id]/send
  • POST /api/mobile-money/withdraw
  • POST /api/njangi/[id]/contribute
  • POST /api/settlements
Sending BTC — PIN in the body
curl https://www.creativmoney.com/api/wallets/send \
  -X POST \
  -H "Cookie: authjs.session-token=..." \
  -H "Content-Type: application/json" \
  -d '{
    "asset": "BTC",
    "amount": 0.001,
    "toAddress": "bc1qsp0kgwneue6y5r2trfvgn80y3y7jna2gmma0na",
    "pin": "1234"
  }'

The PIN travels in the request body — always over HTTPS, never reuse a logged request. The server hashes + constant-time-compares against the bcrypt'd PIN; plaintext is never persisted.

Webhooks

Verify every signed body. Return 401 on mismatch — we retry.

Four webhook endpoints (/api/webhooks/ibex, alchemy, shuftipro, crypto) accept signed POST bodies from external providers. Every one uses the same discipline:

  • Read the raw request body as a string (before JSON.parse).
  • Compute HMAC-SHA256(body, secret).
  • Constant-time-compare against the provider's signature header.
  • Return 401 on any mismatch or missing secret — never 500 with a descriptive error (information disclosure).
  • Write a row to webhook_locks before mutating state. Concurrent retries lose gracefully; only one delivery completes.
Verifying a CreativMoney webhook (Node.js receiver)
import crypto from "node:crypto";

export async function receiveWebhook(req) {
  const body = await req.text();
  const expected = crypto.createHmac("sha256", process.env.WEBHOOK_SECRET)
    .update(body).digest("hex");
  const sig = req.headers.get("x-webhook-signature") ?? "";
  if (expected.length !== sig.length ||
      !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
    return new Response("Unauthorized", { status: 401 });
  }
  const event = JSON.parse(body);
  // ... handle event.type ...
  return new Response("ok");
}

Errors, rate limits, idempotency

What to expect on the failure paths.

Three things to know before shipping a client against this API:

Error shape
Most routes return { "message": string } with the right HTTP status; a few return { "error": string }. Successful responses wrap the payload in { "data": ... }.
Rate limits
32 endpoints are rate-limited (⚡ Rate badge). Limits vary; when exceeded you get HTTP 429 with a retryAfterSec field in the body and a Retry-After header.
Idempotency
Money-moving endpoints persist a unique `reference` on the ledger row. A retry with the same reference trips the DB uniqueness constraint and rolls the write back safely.

Common HTTP codes:

200 / 201
Success. Body is { data: ... } or the raw resource.
207 Multi-Status
Cron sweeper ran, some sub-steps failed. Body has per-bucket counts.
400
Validation error. Body tells you which field is wrong.
401
Missing / invalid auth. Also used for webhooks on signature mismatch OR unconfigured secret (deliberate — can't distinguish from outside).
403
Authenticated but not authorized (e.g., non-admin hitting /api/admin/*).
404
Resource not found. The admin-gated API doc returns 404 when the master toggle is off.
409
State conflict — trade already resolved by another admin, OTP already used, etc.
429
Rate limit. Retry-After header tells you how long to wait.
500
Server bug. Please report with the request-time timestamp.

Pagination

Page + limit query parameters on every list endpoint.

List endpoints (transactions, notifications, trades, audit-logs, etc.) accept `?page=N&limit=M` query parameters. Defaults are sensible (page=1, limit=20-100 per endpoint), caps prevent unbounded reads. The response includes `total`, `page`, `limit`, and `hasMore`.

Paginate through activities
async function* fetchAllActivities() {
  let page = 1;
  while (true) {
    const r = await fetch(`/api/user/activities?limit=50&page=${page}`);
    const { data, hasMore } = await r.json();
    yield* data;
    if (!hasMore) return;
    page++;
  }
}

Endpoints

/auth

9 endpoints
Public
POST
Public Rate
POST
Public

Sign out — clears session cookies.

POSTPUT
Public Rate
POST
User
POST
User
POST
Public Rate

Try to create an IBEX sub-account for the user (non-custodial).

POST
Public Rate

POST /api/auth/reset-password

POST
Public

Legacy OTP verification endpoint.

/user

3 endpoints
GET
User
GETPATCH
User
GETPOST
User

/users

2 endpoints
GETPATCH
User

/wallets

10 endpoints
GET
User
GET
User
POST
User Rate PIN
GET
User
GET
User
POST
User Rate
GET
User
POST
User Rate PIN

Assets eligible for Lightning internal settlement.

POST
User Rate

KYC threshold — kept in sync with `INTERNAL_TRANSFER_KYC_THRESHOLD_USD`

POST
User Rate

POST /api/wallets/send/verify — Verify a Lightning settlement proof.

/trades

10 endpoints
GETPOST
User Rate
GETPATCH
User

PATCH — limited trade mutations that don't involve escrow.

POST
User Rate
POST
User Rate
POST
User Rate
POST
User Rate
POST
User Rate
POST
User Rate
POST
User Rate
GET
User

/chats

4 endpoints
GET
User

GET /api/chats — Combined list of group chats + DM conversations,

GETPOST
User Rate

/api/chats/dm/[userId]

GETPOST
User Rate
DELETE
User

DELETE /api/chats/messages/[messageId]

/notifications

2 endpoints
GETPATCH
User
PATCH
User

/kyc

1 endpoint
GETPOST
User

/groups

6 endpoints
GETPOST
User Rate
DELETEGETPATCH
User

GET /api/groups/:id

GETPOST
User
POST
User
POST
User
DELETEPATCH
User

/api/groups/:id/members/:memberId

/otc

4 endpoints
GET
Public
GETPOST
User Rate

GET /api/otc/offers — List active OTC offers, optionally filtered by group,

POST
User Rate

POST /api/otc/offers/:offerId/execute — Execute an OTC offer by creating a

GETPOST
User Rate

GET /api/otc/ratings?groupId=... — Real OtcRating list + summary for a desk.

/njangi

3 endpoints
GETPOST
User Rate
GET
User

GET /api/njangi/[id] — Njangi group detail.

POST
User Rate PIN

/bank-accounts

2 endpoints
GETPOST
User Rate
DELETEPATCH
User

/mobile-money

3 endpoints
DELETEGETPATCHPOST
User
POST
User

POST /api/mobile-money/verify — Verify a mobile-money account using the

GETPOST
User Rate PIN

POST /api/mobile-money/withdraw — Initiate a mobile-money withdrawal.

/settlements

1 endpoint
GETPOST
User Rate PIN

/support

1 endpoint
POST
User Rate

/exchange-rates

1 endpoint
GET
Public Rate

/rates

1 endpoint
GET
Public

/landing

1 endpoint
GET
Public

Public CMS read endpoint. landing.html fetches this on load and rewrites

/disputes

1 endpoint
GETPOST
User Rate

/business

6 endpoints
GET
User

GET /api/business/broadcast

GET
User

GET /api/business/customers

GET
User

GET /api/business/earnings?months=6

GET
User

GET /api/business/groups

GET
User

GET /api/business/groups/:groupId

GET
User

GET /api/business/overview

/admin

22 endpoints
GET
Admin

GET /api/admin/audit-logs — Paginated read of the audit trail.

GET
Admin

GET /api/admin/business — Business intelligence: top traders, smart links,

GET
Admin
GETPOST
Admin
GETPATCH
Admin

GET /api/admin/docs — read the API-doc visibility config + section list.

GETPUT
Admin
GETPATCHPOST
Admin

POST /api/admin/groups — Admin member management (approve/reject)

GET
Admin
GETPUT
Admin
GETPATCHPOST
Admin
GET
Admin

Admin Reconciliation Endpoint — detect wallet balance drift.

GET
Admin

GET /api/admin/secrets — Secrets health dashboard.

GETPUT
Admin
GETPOST
Admin
GET
Admin
GETPATCHPOST
Admin
GET
Admin
GETPATCH
Admin

GET /api/admin/transactions — All wallet transactions across all users.

GETPOST
Admin

POST /api/admin/transactions/[id]/reconcile

GET
Admin
GETPATCH
Admin

/webhooks

4 endpoints
POST
HMAC

Alchemy Notify webhook handler — receives ADDRESS_ACTIVITY events

POST
HMAC

Webhook endpoint for crypto payment confirmations.

POST
HMAC

IBEX Webhook Handler — receives payment confirmations from IBEX Hub.

POST
HMAC

ShuftiPro Webhook Callback Handler

/cron

2 endpoints
GET
Cron

GET /api/cron/expire-pending-txs — Periodic sweeper for stale PENDING transactions.

GET
Cron

GET /api/cron/expire-trades — Periodic sweeper for stale trades.

/docs

1 endpoint
GET
Public

GET /api/docs/api — public API reference, gated by AppSetting `docs.api.config`.

/push

1 endpoint
POST
User

POST /api/push/register

/trust

2 endpoints
GET
User
GET
User