neo-pays

⚠️

Authentication

Every /v1/* route authenticates with an API key:

Authorization: Bearer sk_live_...

X-API-Key: sk_live_... is accepted identically, for HTTP clients that reserve Authorization for something else.

Test keys and live keys

Prefix Mode Real money
sk_test_… Test No — all the code runs (routing, fees, validation), no money moves
sk_live_… Production Yes

Objects created in test mode carry is_test: true and never appear in your production reports.

Test mode does not cover instant payment. The PI and tontine families refuse test keys: they talk to an external system that has no sandbox on our side.

The secret exists once

When a key is created (or rotated), the secret is shown once. On our side only its HMAC is stored: nobody — not even support — can give it back to you. Lost? Rotate: the old key is revoked and the new one is shown once.

Why does my key answer client_inactive?

A key is issued as soon as you sign up, but it admits no call until the account is approved (KYB file accepted, account active). That is the compliance door: it opens on approval and closes immediately on suspension — the same key, with nothing else changed.

Scopes

Each key carries scopes, and an operation outside them answers 403 insufficient_scope: collect, pay out, refund, read, manage webhooks. Issue the narrowest key that does the job — a read key for your reports has no business being able to pay out.

Idempotency

On creations (POST /v1/payments, POST /v1/payouts, POST /v1/refunds…), pass an Idempotency-Key header of your choosing — your order reference, for instance. A replay with the same key returns the original operation, never a duplicate; the same key with a different payload is refused with 409.

One key per operation, never per parent object. A collection can be refunded in several parts: an idempotency key carrying the payment's identifier would make the second partial refund look like a replay of the first. Success on the merchant's side, customer not refunded.

Rate limit

300 requests per minute per key by default. Beyond that, 429 with a Retry-After header — honour it rather than retrying immediately. The limit is per key, so live and test integrations do not share it. Your dashboard's API keys page shows the limit that applies to your account; if an integration needs more, ask support and it is adjusted for your account, not for a single key.

Payout-creating calls (POST /v1/payouts, /v1/payout-requests, /v1/transfers) may carry a second, tighter per-key limit set for your account; the same 429 and Retry-After apply.

Good practice

  • One key per environment (production, staging), never shared between services.
  • The secret lives in a secret manager, never in code and never in the front end.
  • Rotate at the first doubt — with no outage if you deploy the new key before revoking the old one.