Skip to content
LIQFYdocs
PTEN
Go to dashboard

Official SDKs

Liqfy publishes official clients for Node.js, Python and .NET. They wrap the same /v1 REST API described in the API Reference — anything you can do with curl, you can do without an SDK. What the SDKs add is the part that is easy to get subtly wrong: idempotency, retry policy, and webhook signature verification.

LanguagePackageInstallRuntime
Node.js / TypeScript@liqfy/nodenpm i @liqfy/nodeNode 18+
Pythonliqfypip install liqfyPython 3.8+
.NET / C#Liqfydotnet add package Liqfynetstandard2.0, net8.0

PHP integrators can use the WooCommerce plugin or call the API directly; a standalone Composer package is not published yet.

#What every SDK guarantees

The three clients are behaviourally identical, not merely similar. The webhook signature verification in particular runs against a shared set of test vectors in CI — if Node accepts a signature, Python and .NET accept the same one, byte for byte.

#Authentication

text
apikey: lq_live_…

The environment (test or live) comes from the key itself. You never configure an environment, and no account or merchant identifier is ever passed in or returned.

#Idempotency is required on financial writes

Creating a charge or a payout requires an idempotency key. The SDKs refuse the call without one rather than sending it and hoping.

Derive the key from your order (pedido-1234), never from a random value. Random defeats the entire mechanism: if your request arrived but the response was lost, retrying with a fresh key creates a second charge; retrying with the same key returns the original.

#Retry policy

SituationRetried?
GET / HEAD on 5xx or network erroryes
POST with an idempotency keyyes — same key on every attempt
POST without an idempotency keynever
Any 4xxnever — the request is wrong, retrying will not fix it

Backoff is exponential with full jitter, capped at 8s.

The third row is the one that matters. A network failure does not tell you whether the server processed the request — only that no response came back. Retrying a keyless financial write on that ambiguity is how a customer gets charged twice.

#Quickstart

#Node.js

js
import { LiqfyClient } from '@liqfy/node';

const liqfy = new LiqfyClient({ apiKey: process.env.LIQFY_API_KEY });

const charge = await liqfy.charges.create(
  {
    amount: 15000,                 // R$ 150.00 in centavos — always an integer
    currency: 'BRL',
    payment_method: 'pix',
    customer: { name: 'Maria Silva', document: '12345678901' },
  },
  { idempotencyKey: `pedido-${orderId}` },
);

charge.pix.br_code;                // Pix Copia e Cola

#Python

python
import os
from liqfy import LiqfyClient

liqfy = LiqfyClient(api_key=os.environ["LIQFY_API_KEY"])

cobranca = liqfy.charges.create(
    {
        "amount": 15000,
        "currency": "BRL",
        "payment_method": "pix",
        "customer": {"name": "Maria Silva", "document": "12345678901"},
    },
    idempotency_key=f"pedido-{pedido_id}",
)

cobranca["pix"]["br_code"]

The Python SDK has no runtime dependencies — standard library only. A payments SDK runs inside your process; every transitive package it pulls in becomes supply-chain surface you inherit from us.

#C#

csharp
using Liqfy;

// Register as a SINGLETON — it is thread-safe and reuses its HttpClient.
// One client per request exhausts TCP ports, the classic .NET trap.
var liqfy = new LiqfyClient(Environment.GetEnvironmentVariable("LIQFY_API_KEY")!);

var cobranca = await liqfy.Charges.CreateAsync(new
{
    amount = 15000,
    currency = "BRL",
    payment_method = "pix",
    customer = new { name = "Maria Silva", document = "12345678901" },
}, idempotencyKey: $"pedido-{orderId}");

var brCode = cobranca!.RootElement.GetProperty("pix").GetProperty("br_code").GetString();

#Verifying webhooks

Verification needs no API key — instantiate the webhook helper on its own.

js
// Node — Express with a raw body parser
app.post('/webhooks/liqfy', express.raw({ type: 'application/json' }), (req, res) => {
  if (!liqfy.webhooks.verify(req.body, req.headers['x-liqfy-signature'], secret)) {
    return res.status(401).end();
  }
  const event = JSON.parse(req.body.toString('utf8'));
  res.status(200).end();          // any 2xx confirms delivery
});
python
# Python — Flask
if not webhooks.verify(request.get_data(), request.headers.get("X-Liqfy-Signature"), secret):
    return "", 401
csharp
// C# — minimal API
if (!webhooks.Verify(corpo, req.Headers["X-Liqfy-Signature"], secret))
    return Results.Unauthorized();

Pass the raw bytes. Do not deserialize and re-serialize the body before verifying: any difference in whitespace or key order changes the HMAC and the signature fails. This is the single most common webhook integration bug, in every language.

Signatures older than 5 minutes are rejected by default, which stops a captured POST from being replayed forever. See Webhooks for the signature format and the retry schedule.

#Errors

Every SDK raises typed errors carrying status, code, type and request_id.

Class of failureNodePythonC#
401 / 403LiqfyAuthErrorLiqfyAuthErrorLiqfyAuthException
400 / 422LiqfyValidationErrorLiqfyValidationErrorLiqfyValidationException
409LiqfyConflictErrorLiqfyConflictErrorLiqfyConflictException
No response at allLiqfyNetworkErrorLiqfyNetworkErrorLiqfyNetworkException

Branch on code, never on the message — messages are written for humans and change without notice. code is contract; see Errors for the catalogue.

The network error is deliberately not a subclass of the API error in any of the three SDKs. When no response came back, you do not know whether the server processed the request. Catching both in one branch hides exactly the distinction that decides whether retrying is safe.

Quote the request_id when you contact support — it locates the exact request in our logs.