Integration Guide
This page puts the pieces together. Each step has its own detailed page; what matters here is the order.
Generate and register your key
Generate an Ed25519 key pair, register the public key from the panel, take the kid. The private key stays on your server.
Write the token layer
Put it in one place in your application: a module that builds the canonical string, signs it, mints the token and holds it in cache. Every other call asks that module for a token.
Get this right once and it works for every product — the authentication layer is shared.
This is where first integrations most often get stuck. On a bodyless request you must not add the Digest: line to the canonical string, and the first argument to crypto.sign must be null. → Authentication
Wire up browser telemetry (optional)
If you want device and behavioural signals, add the Identity Web SDK to your page. It is headless — there is no widget to mount.
<script src="https://cdn.lunixi.com/lunixi-identity.min.js"></script>const client = await Lunixi.identity.init({
sessionBootstrapUrl: '/lunixi/identity/session',
subjectId: 'cus_8f2a9c',
productContext: 'checkout',
});
const snapshot = await client.analyze();sessionBootstrapUrl is an endpoint on your server, where you mint the session token. Your secret key never reaches the page.
The SDK is not fail-open: if init(), analyze() or track() fails it throws — it does not silently return a pass. You own the fallback.
Ask for the decision at the right moment
Request the fraud decision before you capture the money. Send whatever context you have — the engine reads only the fields your active rules reference.
POST /api/v1/fraud/decisions/evaluateAct on what comes back:
| Decision | What you do |
|---|---|
allow | Proceed with the payment |
force_3d | Step up to 3DS |
review | Hold for review; do not capture |
block | Decline |
Feed events for history-based rules
Rules like velocity and baseline deviation cannot work from a single transaction. "This customer made 12 transactions in the last 24 hours" comes from the event backbone:
POST /api/v1/fraud/events/transactions (single)
POST /api/v1/fraud/events/transactions/batch (batch)These endpoints are Ed25519 signed, not Bearer. Without an event feed, most of the ready-made templates never fire.
Keep the trace
Write the requestId from every decision response, and the reasoning behind the decision (code, reasonCodes, flowPath), into your own records. These are what get looked up in a dispute, an audit or a support request.
Set up webhooks
Create your endpoint, put the signing secret in your vault, write the verification. In the first version it has one job: verify the signature → write to your queue → return 2xx.
Publish your flow
Do not go live without publishing rules. An unconfigured merchant does not get allow — you get the policy default (typically review) with code fraud.decision.unconfigured, and all of your traffic lands in review.
Go-live checklist
- Private key in a secrets manager, not in the repo or the image
- Token is cached, not minted per call
- The
Authorizationheader is not logged - Error branching goes through
code, notmessage -
requestIdis written to your own logs - Amounts are sent as minor-unit integers
-
Idempotency-Keyon create-payment is derived from your transaction - Webhook signature is verified and consumption is idempotent on
x-lunixi-event-id - You have decided what happens when we are unreachable (fail-open / fail-closed / queue)
- The fraud flow is published and has been watched in shadow mode
-
force_3dgenuinely steps the transaction up to 3DS
Related
- Quickstart — working signing code
- Best Practices — what to watch in production
- Error Handling — envelope, codes, retries