Authentication
Lisoloo authenticates every request with a single header,
app-key: YOUR_API_KEY. The key is per-merchant, per-environment, and
identifies the application as well as authenticating it.
The model in one paragraph
Section titled “The model in one paragraph”Your backend obtains an api_key from the dev portal once. Every call
to $BASE_URL/api/v1/lisoloo/sms-api/* carries that
key in the app-key request header. The gateway narrows by
(group, env, key_type, key_prefix, last4) then runs a constant-time
Argon2id verify against the stored hash. Only keys with status
allowed (and not soft-deleted or revoked) authenticate; any other
status returns 403. The cleartext is shown exactly once at
generation time — re-displays return only the prefix + last 4 digits.
The header
Section titled “The header”POST /api/v1/lisoloo/sms-api/send HTTP/1.1Host: {BASE_URL}app-key: sk_live_kqz3pX9aB7nT2vR4wY8eD1fH6jM5oU0iContent-Type: application/json{BASE_URL} is the host you copy from
Bloonio dashboard → Lisoloo → Developer → API keys.
The dashboard shows separate base URLs for the sandbox and
production environments.
The key format is sk_<env>_<32-char-base62> where env ∈ {test, live}.
The sk_test_* prefix routes to the sandbox; sk_live_* to production.
You cannot use a sandbox key against the production base URL, and vice
versa — see Environments.
Where to keep it
Section titled “Where to keep it”| Storage | Use it? |
|---|---|
| Environment variable on your server | Yes — recommended. |
| Vault / KMS / Secrets Manager | Yes — better for compliance regimes. |
| Hard-coded in source | No — leak risk. |
| Client-side (browser / mobile app) | No — the key has full account scope; assume any client copy is leaked. |
.env checked into git | No. |
If the SMS-sending logic must run from a client, proxy through your own backend. Never ship the key to a browser or app binary.
Authenticated examples
Section titled “Authenticated examples”curl $BASE_URL/api/v1/lisoloo/sms-api/balance \ -H "app-key: $LISOLOO_API_KEY"import osimport requests
API_KEY = os.environ["LISOLOO_API_KEY"]
session = requests.Session()session.headers["app-key"] = API_KEY
balance = session.get( "$BASE_URL/api/v1/lisoloo/sms-api/balance", timeout=10,).json()
print(balance["data"]["available_sms"])const apiKey = process.env.LISOLOO_API_KEY;
const r = await fetch( "$BASE_URL/api/v1/lisoloo/sms-api/balance", { headers: { "app-key": apiKey } },);const { data } = await r.json();console.log(data.available_sms);$apiKey = getenv('LISOLOO_API_KEY');
$ch = curl_init('$BASE_URL/api/v1/lisoloo/sms-api/balance');curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["app-key: $apiKey"],]);$balance = json_decode(curl_exec($ch), true);curl_close($ch);
echo $balance['data']['available_sms'];What the gateway returns on auth failure
Section titled “What the gateway returns on auth failure”The gateway wraps errors in FastAPI’s standard {"detail": "<text>"}
shape. Branch on the HTTP status, not the message text — the messages
are localised and may change.
| HTTP | detail value | Meaning |
|---|---|---|
401 | "Missing app-key header. Please provide a valid app-key." | The app-key header is absent. |
401 | "Empty app-key header. Please provide a valid app-key." | The header is present but empty. |
401 | (generic unauthorized) | The key prefix is wrong for the environment, the key is too short, or the Argon2id verify failed against every candidate. |
403 | "Gateway app is pending validation. Please contact support." | Key in pending_validation. |
403 | "Gateway app is suspended. Please contact support." | Key in suspended. |
403 | "Gateway app has been revoked. Please contact support." | Key in revoqued. |
403 | "Gateway app is locked. Please contact support." | Key in locked. |
403 | "Gateway app has expired. Please contact support." | Key in expired. |
403 | "Gateway app has been rejected. Please contact support." | Key in rejected. |
500 | "Authentication service temporarily unavailable. Please try again." | Unexpected exception during verification. Retry. |
The middleware deliberately doesn’t tell you which candidate failed verification — it only confirms that no row matches, to avoid leaking key-prefix collisions.
Rotating an API key
Section titled “Rotating an API key”The dev portal lets you regenerate the key in one click. The new key is shown in cleartext on the same one-time banner pattern as initial generation. The old key is invalidated immediately — there is no overlap window, so plan the cutover:
- Generate a new key and copy the cleartext.
- Update your secret store and roll your service.
- Confirm traffic is flowing on the new key (you’ll see
last_used_atadvance on the key row). - The old key is already dead — nothing else to do.
If you need a true zero-downtime rotation with an overlap window, contact your Bloonio account manager — the underlying key model supports multiple active keys per merchant; the portal exposes only the single-key rotation flow.
See also
Section titled “See also”- API keys — full management UI walkthrough
- Environments — sandbox vs. production
- Webhooks → Configuration — pair the key with a delivery URL
- Errors — the full error response shape