Lu71

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_KEY

Once installed, your agent gets these tools:

lu71_capture_intent

Call 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_dispute

File 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_dispute

Check 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_disputes

List 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_status

Check 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_status

Diagnose 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/sdk

Quick 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 arrived
not_as_describedItem received does not match what was ordered
duplicateCharged more than once for the same purchase
unauthorizedCharge was not authorized by the cardholder
cancelledOrder was cancelled but payment was still taken
service_not_renderedService was paid for but never provided
otherDoes not fit the above categories

Webhook 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.submitted

Dispute has been filed with the card network. Evidence submitted.

dispute.under_review

Card network is actively reviewing the evidence. Typically takes 1 to 4 weeks.

dispute.won

Dispute resolved in your favour. Funds returned to cardholder. Success fee charged.

dispute.lost

Dispute 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")