Skip to content

Developers

Build once on the payment orchestration layer.

One API pattern across every provider and product behind LILPE. Routing, retries and failover happen on our side; your code sees one payment and one final status. Sandbox access comes with onboarding, and the LILPE team stays with you through go-live.

Works from any language that can make HTTPS requests:

  • cURL
  • Node.js
  • Python
  • PHP
  • Java
  • Go
Illustrative example

POST /v1/payments

curl https://api.lilpe.com/v1/payments \
  -H "Authorization: Bearer $LILPE_SECRET_KEY" \
  -H "Idempotency-Key: order_10234" \
  -d amount=124900 \
  -d currency=EUR \
  -d "customer[country]=DE" \
  -d "routing[strategy]=smart"
const res = await fetch("https://api.lilpe.com/v1/payments", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.LILPE_SECRET_KEY}`,
    "Idempotency-Key": "order_10234",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    amount: 124900,
    currency: "EUR",
    customer: { country: "DE" },
    routing: { strategy: "smart" },
  }),
});
const payment = await res.json();
import os, requests

payment = requests.post(
    "https://api.lilpe.com/v1/payments",
    headers={
        "Authorization": f"Bearer {os.environ['LILPE_SECRET_KEY']}",
        "Idempotency-Key": "order_10234",
    },
    json={
        "amount": 124900,
        "currency": "EUR",
        "customer": {"country": "DE"},
        "routing": {"strategy": "smart"},
    },
).json()

Response 201 Created 184 ms

{
  "id": "pay_8Hk2Qm",
  "status": "authorized",
  "amount": 124900,
  "currency": "EUR",
  "provider": "provider_a",
  "route": {
    "rule": "high_value_eu_cards",
    "attempts": 1,
    "fallback": "provider_c"
  }
}

webhook → your endpointsigned

payment.authorized · pay_8Hk2Qm

Developer toolkit

Everything you need from first request to production.

Preview marks dashboard tools shown as a demo of the developer experience. Which tools are available for your business is confirmed during onboarding.

Quick start

From keys to your first webhook

The four steps every integration takes. Code is illustrative; the API reference you get with sandbox access has the exact names and fields.
  1. Step 1Get sandbox keys

    Sandbox and production use separate keys and data. Sandbox credentials come with onboarding.

    .env

    LILPE_SECRET_KEY=sk_test_•••••••• # server-side only
  2. Step 2Authenticate

    Send the secret key from your server as a bearer token. Never ship it to a browser or app.

    HTTP header

    Authorization: Bearer $LILPE_SECRET_KEY
  3. Step 3Create a payment

    One request shape for every provider. Send an Idempotency-Key so a retried request returns the same payment.

    Request

    POST /v1/payments
    Idempotency-Key: order_10234
    { "amount": 124900, "currency": "INR",
      "reference_id": "order_10234" }
  4. Step 4Handle the webhook

    Verify the signature with your secret, act on the event once, and reply 2xx. Unacknowledged deliveries are retried.

    Webhook handler

    verify(lilpe-signature,
      hmac_sha256(secret, timestamp + "." + body))
    // then mark the order paid, once

API overview

Core endpoints

Payments, refunds, payouts, links and settlements share the same authentication, request shape and status model. Statuses include created, processing, authorized, failed, refunded and partially_refunded.
  • POST/v1/payments

    Create a payment; routed across the providers connected for your business.

  • GET/v1/payments/{id}

    Status, attempts and provider references for one payment.

  • POST/v1/refunds

    Full or partial refund, back through the original provider.

  • POST/v1/payouts

    Send money to a bank account or UPI ID.

  • POST/v1/payment_links

    Create a payment link with an expiry and reference.

  • GET/v1/settlements/{id}

    Settlement lines, fees and GST for reconciliation.

Base URL and endpoints are illustrative; the full reference comes with sandbox access.

Error handling

One error shape, whichever provider failed

Provider responses are mapped to a small set of categories, and every error says whether a retry makes sense.

Error object · illustrative

{
  "error": {
    "type": "payment_failed",
    "category": "issuer_unavailable",
    "message": "The customer's bank didn't respond.",
    "retryable": true,
    "provider": "provider_a",
    "provider_code": "U30"
  }
}
Failure categories and whether a retry helps
CategoryRetryMeaning
provider_errorUsuallyThe provider failed on its side.
timeoutAfter a status checkNo answer in time; checked before any retry.
issuer_unavailableUsuallyThe customer's bank or issuer didn't respond.
customer_declineUsually notDeclined by the customer or their bank for a reason a retry won't fix.
risk_declineUsually notStopped by a risk check.

Technical architecture

What happens between your request and the provider

Your server calls LILPE. The routing engine picks an eligible provider, LILPE talks to it in its own format, and the outcome comes back in one shape, by API and by signed webhook.
  1. Your serverSecret key, server-side
  2. LILPEOne API, one webhook stream
  3. Routing engineRules, health, cost
  4. ProvidersEach in its own format
  5. Payment networksCards, real-time rails, banks
Illustrative flow. Which providers are connected for your business is confirmed during onboarding.

Integration Guide

From sandbox to production, with guided support

  1. 1

    Get Sandbox Access

    Sandbox credentials are provided during onboarding, after the business evaluation.

  2. 2

    Integrate the API

    Use the same request pattern across whichever LILPE products your business needs.

  3. 3

    Test End-to-End

    Run test transactions, payouts, or verification calls in the sandbox environment.

  4. 4

    Go Live

    Move to production credentials, with the LILPE team on hand for go-live.

Any language that can make HTTPS requests works, for example:

cURLNode.jsPythonPHPJava
Illustrative request
curl -X POST https://api.lilpe.com/v1/payments \
  -H "Authorization: Bearer <LILPE_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 149900,
    "currency": "INR",
    "order_id": "ORDER_10234",
    "payment_methods": ["upi", "card", "netbanking"]
  }'

API Reference

A consistent request pattern across every product

A sample of the core endpoints businesses integrate against most — collection, payouts, verification, and account data all follow the same authentication and request shape.

POST/v1/collections/order
Illustrative request
curl -X POST https://api.lilpe.com/v1/collections/order \
  -H "Authorization: Bearer <LILPE_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 249900,
    "currency": "INR",
    "order_id": "ORDER_10234",
    "payment_methods": ["upi", "card", "netbanking"]
  }'
Illustrative response
{
  "order_id": "order_SP9V21x",
  "status": "created",
  "amount": "₹2,499.00",
  "checkout_url": "checkout.lilpe.com/pay/9V21x"
}

API & Security

Security built into every request

TLS encryption on every API request
Separate sandbox and production credentials
API keys scoped per environment, never exposed client-side
Webhook notifications for status changes
Role-based access and MFA for dashboard users
Audit trails for credential and configuration changes

Integration practices

What makes a payment integration last

These apply to any payment integration, with LILPE or anyone else. Most production incidents in payments come from skipping one of them.

Treat notices as the source of truth

A customer's browser returning to your site doesn't prove payment. Mark orders paid from the server-to-server notice (webhook) or a status check, never from the redirect alone.

Verify every notice

Check the signature on each webhook with your secret before acting on it, and reject anything that fails. Anyone can send a request to a public URL.

Expect duplicates and disorder

Notices can arrive twice, late or out of order. Make handling idempotent: processing the same event twice must have no extra effect, and a later status must not be overwritten by an earlier one.

Use idempotency keys for money-moving requests

When you retry a payout or refund after a timeout, send the same idempotency key so the request can't be executed twice.

Handle 'pending' as a real state

Some payments take time to reach a final status. Don't show failure, and don't ask the customer to pay again, until the status is final.

Keep secrets on the server

API keys and webhook secrets belong in server-side configuration, never in browser or app code, and are rotated when people with access leave.

Test cases to run before go-live
TestExpect
Successful paymentOrder paid from the notice; confirmation sent once
Declined paymentClear message; customer can retry without a second order
Customer closes the tab after payingOrder still paid from the notice
Same notice delivered twiceNo duplicate fulfilment or email
Refund, full and partialCustomer credited; your records updated
Request times out and is retriedNo duplicate payout or refund, thanks to the idempotency key

A worked example of notices and status checks: collection API handbook.

Guides

Guides and explainers

Plain-language answers to the questions businesses ask before and after they go live.

Ready to start building?

Request sandbox access and we'll set you up with test keys and the API reference.