Getting Started

Authentication

Every LuniPay API request is authenticated with a prefixed key over HTTPS. Keep your secret keys on the server, never in a browser or a mobile app.

Key types

LuniPay issues four keys per organization. The prefix tells you at a glance whether a key is secret or publishable, test or live — no database lookup required.

PrefixModeWhere it livesCan do
sk_test_…TestServer onlyEverything
sk_live_…LiveServer onlyEverything
pk_test_…TestBrowser / mobileRetrieve checkout sessions only
pk_live_…LiveBrowser / mobileRetrieve checkout sessions only

Secret keys never touch the browser

A leaked sk_… key gives an attacker full API access to your account. Use publishable keys for any code that runs on a device you do not control, and rotate secrets immediately if you think one has leaked.

Sending a request

Pass the key in the Authorization header as a bearer token. Every request must use HTTPS and should call the www host directly.

curl https://www.lunipay.io/api/v1/customers \
  -H "Authorization: Bearer sk_test_YOUR_KEY"

Use www.lunipay.io for API calls

The supported API base URL is https://www.lunipay.io/api/v1. Do not call https://lunipay.io/api/v1 from code. The apex domain can redirect to www, and most HTTP clients dropAuthorization when a redirect crosses hosts.

Storing keys

Never commit a secret key to git. Put it in environment variables, a secret manager (Vercel, Doppler, AWS Secrets Manager), or your CI provider's secret store. The secret value is shown exactly once on creation — if you lose it, rotate from the dashboard rather than digging through old logs.

Rotating keys

Rotation is a single click from Settings → Developer. The old key is revoked immediately and a new one is generated. Deploy the new key before rotating, or keep both keys configured for a short window.

Live vs test keys

Test and live modes share the same schema, but no data crosses between them. Every database row carries a livemode boolean; every API response includes it; every query filters on it. Practically that means:

  • A test customer is invisible to live-mode queries and vice versa.
  • A webhook endpoint registered in test mode never fires for live events.
  • Refunds, ledger entries, payouts — everything respects the livemode your key was issued for.
Build and test with sk_test_… keys until you are confident. No real charges ever happen in test mode, and the dashboard shows a bright orange banner so you can always tell which mode you are in.

Keys are scoped to the environment

A key minted by a local or self-hosted LuniPay instance will not authenticate against www.lunipay.io, and hosted LuniPay keys will not authenticate against your local instance. If you see invalid_key, check both the key prefix and the API host.

Authentication errors

If the Authorization header is missing or malformed, LuniPay returns 401 missing_key. The error body follows the standard shape:

{
  "error": {
    "type": "authentication_error",
    "code": "missing_key",
    "message": "Missing or malformed Authorization header. Expected: Bearer {api_key}. Use https://www.lunipay.io/api/v1 directly; requests to lunipay.io may redirect and lose the Authorization header."
  }
}

If the header is present but the key does not belong to this environment, LuniPay returns 401 invalid_key.

{
  "error": {
    "type": "authentication_error",
    "code": "invalid_key",
    "message": "Invalid API key provided. Check that the key belongs to this LuniPay environment and API host."
  }
}