Documentation
Everything you need to integrate Lu71 into your agent platform.
MCP Setup
The recommended way to add Lu71. One command installs dispute tools into any MCP compatible AI agent.
shellnpx @lu71/install --key=lu71_live_YOUR_KEYOnce installed, your agent gets these tools:
lu71_capture_intentCall BEFORE every purchase. Records what the agent intends to buy and returns a signed receipt (intentId + signature). This receipt is required when filing disputes. Without it, the dispute will be rejected.
lu71_file_disputeFile a chargeback when a purchase goes wrong. Requires the intentId and signature from capture_intent, plus the transaction ID from the card platform (Stripe or Lithic). Provide a clear description of what went wrong.
lu71_check_disputeCheck the current status of a filed dispute. Returns one of: submitted (filed, waiting), under_review (card network investigating), won (money returned), or lost (no charge to you).
lu71_list_disputesList all disputes for this account with full details including status, amount, reason, platform, and dates. Use this to give the user an overview or find a specific dispute.
lu71_connect_statusCheck which payment platforms are connected (Stripe, Lithic) and their connection method. If nothing is connected, direct the user to the dashboard to set up a platform.
lu71_account_statusDiagnose account setup issues. Returns 2FA status, number of active API keys, and connected platforms. Use this when the user encounters authentication or configuration errors.
Manual config (Claude Desktop):
json{
"mcpServers": {
"lu71": {
"command": "npx",
"args": ["-y", "@lu71/mcp", "--key=lu71_live_YOUR_KEY"]
}
}
}TypeScript SDK
Install with any package manager:
shellnpm install @lu71/sdk
# or
pnpm add @lu71/sdk
# or
bun add @lu71/sdkQuick start:
typescriptimport { Lu71 } from "@lu71/sdk"
const lu71 = new Lu71({ apiKey: "lu71_live_..." })
// 1. Capture intent before purchase
const { intentId, signature } = await lu71.captureIntent({
description: "Buy 100 blue widgets",
merchant: "WidgetCo",
maxAmount: 5000,
})
// 2. Agent makes purchase with its card...
// 3. File dispute when something goes wrong
const dispute = await lu71.fileDispute({
intentId,
intentSignature: signature,
transactionId: "ipi_xxx",
reason: "not_as_described",
description: "Received red widgets instead of blue",
})API Reference
All requests require an x-api-key header except signup and webhooks. Click any endpoint to see full request and response details.
Creates a customer account. Returns an API key for authentication. After signup, enable 2FA and add a payment method to generate production keys.
Request
json{
"name": "Acme Corp",
"email": "dev@acme.com",
"password": "min6chars"
}Response
json{
"message": "Account created.",
"customerId": "cus_abc123",
"apiKey": "lu71_live_..."
}Call this BEFORE every purchase your agent makes. It creates a cryptographically signed record of what the agent plans to buy. The returned intentId and signature are required when filing a dispute later. Without them, disputes will be rejected.
Request
json{
"description": "Buy 100 blue widgets from WidgetCo",
"merchant": "WidgetCo",
"maxAmount": 5000,
"currency": "gbp",
"constraints": { "color": "blue", "quantity": "100" }
}Response
json{
"message": "Purchase intent captured.",
"intentId": "int_abc123",
"signature": "hmac_sha256_...",
"createdAt": "2026-06-05T10:00:00Z"
}File a chargeback when a purchase goes wrong. You must provide the intentId and signature from a previous capture_intent call, plus the transaction ID from your card platform (Stripe or Lithic). The description becomes part of the evidence submitted to the card network, so be factual and specific.
Request
json{
"intentId": "int_abc123",
"intentSignature": "hmac_sha256_...",
"transactionId": "ipi_xxx",
"reason": "not_as_described",
"description": "Received red widgets instead of blue",
"evidence": {
"whatWasReceived": "100 red widgets",
"productType": "merchandise",
"receivedAt": "2026-06-06T14:00:00Z",
"returnStatus": "merchant_rejected"
}
}Response
json{
"message": "Dispute filed.",
"disputeId": "dsp_abc123",
"status": "submitted",
"platformDisputeId": "idp_xxx"
}Returns the full details of a single dispute including status, evidence, platform info, and resolution dates.
Response
json{
"id": "dsp_abc123",
"status": "won",
"amount": 5000,
"currency": "gbp",
"reason": "not_as_described",
"platform": "stripe",
"transactionId": "ipi_xxx",
"description": "Received red widgets instead of blue",
"filedAt": "2026-06-05T10:30:00Z",
"resolvedAt": "2026-06-20T09:00:00Z"
}Returns all disputes for the authenticated account, ordered by most recent. Each dispute includes full details.
Response
json[
{ "id": "dsp_abc123", "status": "won", "amount": 5000, ... },
{ "id": "dsp_def456", "status": "submitted", "amount": 2000, ... }
]Returns a URL to redirect your user to Stripe's OAuth consent screen. After authorization, Stripe redirects back and we store the connected account ID. We never store your Stripe API key.
Response
json{
"url": "https://connect.stripe.com/oauth/authorize?...",
"message": "Redirect customer to this URL"
}Connect a Lithic account using a restricted API key. The key should have disputes:write scope. We validate it against Lithic's API before storing.
Request
json{ "apiKey": "li_restricted_..." }Response
json{ "message": "Lithic connected. Using restricted API key." }Register a URL where we will POST dispute status updates. Returns a signing secret you can use to verify that incoming webhooks are genuinely from Lu71.
Request
json{ "url": "https://your-app.com/webhooks/lu71" }Response
json{
"message": "Webhook configured.",
"webhookUrl": "https://your-app.com/webhooks/lu71",
"webhookSecret": "whsec_...",
"events": ["dispute.submitted", "dispute.under_review", "dispute.won", "dispute.lost"]
}Dispute Reasons
Use one of these reason codes when filing a dispute:
not_receivedItem was paid for but never arrivednot_as_describedItem received does not match what was orderedduplicateCharged more than once for the same purchaseunauthorizedCharge was not authorized by the cardholdercancelledOrder was cancelled but payment was still takenservice_not_renderedService was paid for but never providedotherDoes not fit the above categoriesWebhook Events
Register a URL via POST /v1/connect/webhooks. We send a POST request to your URL whenever a dispute changes status. Each payload is signed with your webhook secret so you can verify authenticity.
json{
"id": "evt_1717578600000",
"type": "dispute.won",
"created": "2026-07-02T09:30:00Z",
"data": {
"disputeId": "dsp_abc123",
"status": "won",
"amount": 5000,
"currency": "gbp",
"reason": "not_as_described",
"resolvedAt": "2026-07-02T09:30:00Z"
}
}Available events:
dispute.submittedDispute has been filed with the card network. Evidence submitted.
dispute.under_reviewCard network is actively reviewing the evidence. Typically takes 1 to 4 weeks.
dispute.wonDispute resolved in your favour. Funds returned to cardholder. Success fee charged.
dispute.lostDispute resolved in the merchant's favour. No fee charged.
Verify webhook signatures:
typescriptimport crypto from "crypto"
function verifyWebhook(body: string, signature: string, secret: string): boolean {
const expected = crypto.createHmac("sha256", secret).update(body).digest("hex")
return signature === expected
}
// In your webhook handler:
const sig = req.headers["x-lu71-signature"]
const valid = verifyWebhook(req.body, sig, "whsec_your_secret")
if (!valid) return res.status(401).send("Invalid signature")