Skip to content

Webhook configuration

Webhook routing lives on your API key. The dev portal exposes three fields:

FieldRequired?Persisted?
webhook_urlYes (to enable webhooks at all)Plaintext, returned by GET on the key.
webhook_basic_auth_usernameNoPlaintext, returned by GET.
webhook_basic_auth_passwordNoWrite-only. Encrypted at rest; never re-displayed.

The URL must be https://http:// is rejected. There is no further validation at submit time (the gateway will not pre-flight your endpoint); the first webhook event that fails to deliver will be retried per the overview and then dropped.

webhook_url: https://merchant.example.com/lisoloo/webhook

Recommended path conventions:

  • Use a path that identifies the source (/lisoloo/) so multi-tenant webhook handlers can route.
  • Include a per-environment subdomain (api.staging.example.com vs. api.example.com) so test traffic can’t accidentally hit production consumers.

The gateway sends Authorization: Basic <base64(username:password)> on every webhook POST when both username and password are configured. Your endpoint should verify this matches the value you set in the portal.

webhook_basic_auth_username: lisoloo_webhook_user
webhook_basic_auth_password: •••••••••••••••••••• (write-only)

The password field is a <p-password> input in the portal — the value you type is sent once and stored encrypted; the next time you view the form the password is not pre-populated. Editing the username without re-entering the password leaves the existing password in place.

To rotate the password: re-enter both username and password and save. The old credentials are invalidated immediately.

To clear Basic auth entirely: click Clear basic auth credentials. The gateway then sends webhooks without an Authorization header.

Standard HTTP Basic verification — decode the Authorization: Basic header, split on :, compare to your expected values using a constant-time comparison.

import base64
import hmac
from fastapi import FastAPI, Header, HTTPException, Request
app = FastAPI()
EXPECTED_USER = os.environ["LISOLOO_WEBHOOK_USER"]
EXPECTED_PASS = os.environ["LISOLOO_WEBHOOK_PASS"]
@app.post("/lisoloo/webhook")
async def webhook(req: Request, authorization: str = Header(None)):
if not authorization or not authorization.startswith("Basic "):
raise HTTPException(401)
raw = base64.b64decode(authorization.split(" ", 1)[1]).decode()
user, _, password = raw.partition(":")
if not hmac.compare_digest(user, EXPECTED_USER) or \
not hmac.compare_digest(password, EXPECTED_PASS):
raise HTTPException(401)
# … handle event …
return {"ok": True}
import express from "express";
const app = express();
app.use(express.json());
const EXPECTED_USER = process.env.LISOLOO_WEBHOOK_USER;
const EXPECTED_PASS = process.env.LISOLOO_WEBHOOK_PASS;
app.post("/lisoloo/webhook", (req, res) => {
const auth = req.headers.authorization ?? "";
if (!auth.startsWith("Basic ")) return res.sendStatus(401);
const [user, pass] = Buffer.from(auth.slice(6), "base64")
.toString()
.split(":");
if (user !== EXPECTED_USER || pass !== EXPECTED_PASS) {
return res.sendStatus(401);
}
// … handle event …
res.sendStatus(204);
});

When you GET the API key row from the dev portal, the response includes webhook_basic_auth_configured: true | false. This is the only signal that credentials are set; the actual password is never returned. The portal UI uses this flag to render the Basic auth configured badge on the API-key card.

Section titled “Allow-list (firewall) — optional but recommended”

If your edge firewall allow-lists outbound IPs, add the Lisoloo egress ranges. Contact your Bloonio account manager for the current list — it’s published outside the docs so it can change without rebuilding the site.