Developer portal

Build on Keelstone

One REST API for accounts, payments, FX, and cards. Requests are idempotent by default, webhooks are signed, and the sandbox mirrors production behavior down to settlement timing — so the first payment you ship behaves like the millionth.

RESTWebhooksSandbox-first4 SDKs

$ curl https://api.keelstone.example/v1/ping

Authentication

Every request is authenticated with a bearer key in the Authorization header. Keys are scoped per environment: mk_sandbox_ keys can never move real money, and mk_live_ keys require an approved workspace. Pin a release with the Keelstone-Version header — breaking changes only ship in new dated versions.

GET /v1/me
curl https://api.keelstone.example/v1/me \
  -H "Authorization: Bearer mk_sandbox_4q9zr31kp7v2m8c5x0jw6t1d" \
  -H "Keelstone-Version: 2026-05-01"

Sandbox key generator

Mint a key shaped exactly like the real thing and use it while you read along. Keys generated here are decorative — demo only, never stored, and not valid against any API.

Payments

Create a payment with POST /v1/payments. Amounts are integers in minor units, and the platform picks the cheapest rail that meets your deadline unless you pin one. Send an Idempotency-Key with every create call — retries with the same key return the original payment instead of paying twice.

POST /v1/payments
curl -X POST https://api.keelstone.example/v1/payments \
  -H "Authorization: Bearer $KEELSTONE_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ord-84312-payout" \
  -d '{
    "amount": 125000,
    "currency": "EUR",
    "beneficiary": "ben_8f2k1x",
    "rail": "sepa_instant",
    "reference": "Invoice INV-2041"
  }'

Treasury

Read balances across every currency account with GET /v1/balances. Each balance splits into available and pending, both in minor units, so your reconciliation never sees a float. Sweeps, yield allocations, and FX conversions live under the same /v1/treasury namespace.

GET /v1/balances
curl "https://api.keelstone.example/v1/balances?currencies=USD,EUR,GBP" \
  -H "Authorization: Bearer $KEELSTONE_API_KEY"

Try it live

GET /api/v1/ping

This one is real: the button calls the demo API served by this site and prints the raw response below.

Send the request to see a live JSON response here.

Webhooks

Subscribe an HTTPS endpoint to any event family and Keelstone pushes state changes to you. Every delivery carries a Keelstone-Signature header — an HMAC-SHA256 of the raw body with your per-endpoint secret. Verify it before trusting the payload, and always compare with a timing-safe function. Failed deliveries retry with exponential backoff for 72 hours.

Verify a webhook signature
# Register an HTTPS endpoint for the events you care about
curl -X POST https://api.keelstone.example/v1/webhook_endpoints \
  -H "Authorization: Bearer $KEELSTONE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/keelstone",
    "events": ["payment.completed", "payment.failed"]
  }'

Event catalog

Flip the toggles to sketch a subscription set — this table is a local simulation, nothing is saved.

EventDescriptionStatusSimulate
payment.completedA payment settled on the destination rail.Subscribed
payment.failedA payment was rejected; payload includes a machine-readable failure code.Subscribed
payment.returnedFunds came back after settlement, for example a closed beneficiary account.Off
invoice.paidAn issued invoice was reconciled against an incoming payment.Off
invoice.overdueAn invoice passed its due date without full payment.Off
treasury.sweep.executedAn automated sweep moved idle balance according to your treasury policy.Off
card.authorization.createdA corporate card authorization is pending; respond within two seconds to approve or decline.Off
payout.batch.settledEvery payment in a batch reached a terminal state.Off

Errors

Errors are conventional HTTP statuses with a structured JSON body: a stable type, a machine-readable code, a human-readable message, and a request_id you can quote to support. 4xx means fix the request; 5xx means retry with backoff and the same idempotency key.

Error shape
curl -i https://api.keelstone.example/v1/payments/pay_unknown \
  -H "Authorization: Bearer $KEELSTONE_API_KEY"

# HTTP/2 404
# {
#   "error": {
#     "type": "resource_missing",
#     "code": "payment_not_found",
#     "message": "No payment found with id pay_unknown.",
#     "request_id": "req_7fk2m1qx"
#   }
# }

Ship it with confidence

Watch every rail and region on the live status page, or browse the FAQ for sandbox limits, webhook retries, and SDK release policy.