# Lumo B2B Platform API

API reference for **company-facing** endpoints of Lumo B2B platform.

Scope of this reference:
- Company auth/profile
- Orders (prepare/accept/list/get)
- Company disputes
- Company wallet + deposits
- User wallets (per externalId) + user deposits


Version: 1.0.0

## Servers

Production
```
https://b2b.lumowallet.io
```

Staging
```
https://b2b-staging.lumowallet.io
```

## Security

### apiKeyAuth

Company API key.
Service also accepts `Authorization: Bearer <api-key>` and `api_key` query param,
but `X-API-Key` is recommended.


Type: apiKey
In: header
Name: X-API-Key

## Download OpenAPI description

[Lumo B2B Platform API](https://b2b-app.lumowallet.io/_bundle/apis/index.yaml)

## Company Auth

Company registration, login, and profile management.

### Company login

 - [POST /auth/company/login](https://b2b-app.lumowallet.io/apis/company-auth/logincompany.md): Authenticates company by email/password and rotates API key.
Previous key becomes invalid after successful login.

### Get company profile

 - [GET /auth/company/profile](https://b2b-app.lumowallet.io/apis/company-auth/getcompanyprofile.md)

### Update company profile

 - [PATCH /auth/company/profile](https://b2b-app.lumowallet.io/apis/company-auth/updatecompanyprofile.md)

### Regenerate company API key

 - [POST /auth/company/regenerate-key](https://b2b-app.lumowallet.io/apis/company-auth/regeneratecompanyapikey.md): Rotates and returns a new API key for current company.

## Rates

Live USDT/RUB rate (Rapira-derived, with per-company QR commission).

### Current USDT/RUB rate (with company commission)

 - [GET /rates/current](https://b2b-app.lumowallet.io/apis/rates/getcurrentrate.md): Returns the live rate used to price QR orders. The base rate is
sourced from Rapira and refreshed every ~15 sec; effectiveRate
applies the company's qrRateOffsetBps (basis points). Both
rate and effectiveRate already include the commission — they
are the value the next /orders/prepare will use.

Also returns the configured minAmountRub / maxAmountRub
bounds enforced when preparing an order.

## Orders

Quote preparation and order lifecycle operations.

### Prepare order (create quote)

 - [POST /orders/prepare](https://b2b-app.lumowallet.io/apis/orders/prepareorder.md): Creates quote from NSPK QR code.
Quote TTL is short-lived (30 seconds in current implementation).

### Accept quote and create order

 - [POST /orders/accept/{id}](https://b2b-app.lumowallet.io/apis/orders/acceptorder.md): Converts quote into order and reserves funds (frozenBalance).

Idempotency:
- Optional header Idempotency-Key.
- Repeated request with same key for same company returns stored response.

### List company orders

 - [GET /orders](https://b2b-app.lumowallet.io/apis/orders/listorders.md)

### Get order by id

 - [GET /orders/{id}](https://b2b-app.lumowallet.io/apis/orders/getorder.md)

## Disputes

Company dispute creation and listing.

### Open dispute for order

 - [POST /orders/{id}/disputes](https://b2b-app.lumowallet.io/apis/disputes/createdispute.md): Allowed only for completed order states (success or failed).

Behavior:
- On create, order is moved to disputed.
- If an open dispute already exists for the order (submitted / in_review), API returns that dispute.
- Dispute is also sent to wallet-backend for synchronized processing.

### List company disputes

 - [GET /disputes](https://b2b-app.lumowallet.io/apis/disputes/listdisputes.md): Returns company dispute history (newest first).

Resolution fields:
- resolvedAt is set when dispute is resolved.
- resolution is set on resolve (confirm, refund, partial_refund).
- refundAmount is set only for partial_refund; for confirm/refund it is null.

Disputes can be resolved by admin flow or automatically from wallet callback final status.

## Company Wallet

Company deposit wallet, balances, and deposit history.

### Get company deposit wallet

 - [GET /wallets](https://b2b-app.lumowallet.io/apis/company-wallet/getcompanywallet.md): Returns company TRON wallet address for incoming USDT (TRC-20).
Wallet is auto-created on first request if absent.

### Get wallet balance view

 - [GET /wallets/balance](https://b2b-app.lumowallet.io/apis/company-wallet/getcompanywalletbalance.md): Returns company balance view.
usdtBalance reflects company balance used by platform accounting.

### List company deposits

 - [GET /wallets/deposits](https://b2b-app.lumowallet.io/apis/company-wallet/listcompanydeposits.md): Deposit status lifecycle:
- new
- fee_charged
- aml_pending / aml_passed / aml_failed
- forwarding
- completed / failed

Company deposit sweep target is B2B_TREASURY_ADDRESS.
Processing fee is retained from incoming deposit amount (not debited from company balance).

## User Wallets

Per-user wallet management and user deposit history.

### Create user wallet

 - [POST /user-wallets](https://b2b-app.lumowallet.io/apis/user-wallets/createuserwallet.md): Creates or returns wallet for given externalId within company scope.

### List user wallets

 - [GET /user-wallets](https://b2b-app.lumowallet.io/apis/user-wallets/listuserwallets.md)

### List blocked user wallets

 - [GET /user-wallets/blocked](https://b2b-app.lumowallet.io/apis/user-wallets/listblockeduserwallets.md)

### Block user wallet

 - [POST /user-wallets/{walletId}/block](https://b2b-app.lumowallet.io/apis/user-wallets/blockuserwallet.md)

### Replace user wallet

 - [POST /user-wallets/{walletId}/replace](https://b2b-app.lumowallet.io/apis/user-wallets/replaceuserwallet.md): Blocks old wallet and creates a new one for the same external user.

### List user-wallet deposits

 - [GET /user-wallets/deposits](https://b2b-app.lumowallet.io/apis/user-wallets/listuserwalletdeposits.md)

## Cards

Virtual VISA card issuance and lifecycle — request, retire, reveal
PAN/CVC, poll 3DS OTP, list transactions.


### List company cards

 - [GET /cards](https://b2b-app.lumowallet.io/apis/cards/listcards.md)

### Request a new card

 - [POST /cards/request](https://b2b-app.lumowallet.io/apis/cards/requestcard.md): Reserves cardOpeningFeeUsdt + cardMinInitialDepositUsdt on the
company balance (frozenBalance += reserve). If the bridge has
a card in stock — returns status: assigned and immediately
debits openingFee from balance. Otherwise returns
status: pending and the request gets fulfilled asynchronously
via card.assigned webhook.

### Get card by id

 - [GET /cards/{id}](https://b2b-app.lumowallet.io/apis/cards/getcard.md)

### Retire a card

 - [POST /cards/{id}/retire](https://b2b-app.lumowallet.io/apis/cards/retirecard.md): Closes the card. Further recharges are rejected with CARD_RETIRED.
The underlying issuer card itself keeps working — retire is a
Lumo-side block only.

### Latest 3DS OTP

 - [GET /cards/{id}/otp/latest](https://b2b-app.lumowallet.io/apis/cards/getlatestotp.md): Returns the freshest 3DS verification code captured from the
acquirer mailbox. Polls run every 15s. Returns null if no
live code. TTL 2 min.

### Live card balance

 - [GET /cards/{id}/balance](https://b2b-app.lumowallet.io/apis/cards/getcardbalance.md): Proxies the current card balance from the issuer (bridge-side
cache, ~60 sec). Only allowed for status: assigned cards.
In cardsTestMode returns a synthetic value (123.45 USD).

### Card transaction history

 - [GET /cards/{id}/transactions](https://b2b-app.lumowallet.io/apis/cards/listcardtransactions.md): Reverse-chronological list. Cursor pagination via before
(records with transactionTime < before).

### Full PAN + CVC (on-demand reveal)

 - [GET /cards/{id}/sensitive](https://b2b-app.lumowallet.io/apis/cards/revealsensitive.md): Proxies directly to the bridge. Nothing is cached by Lumo. Only
allowed for status: assigned cards — retired/failed/requested
return 404. Every reveal is logged (company + card).

## Card Recharges

Quote-then-execute card top-ups, recharge history.

### Recharge cost quote

 - [POST /cards/recharge/quote](https://b2b-app.lumowallet.io/apis/card-recharges/quoterecharge.md): Calculates the total USDT debit for a recharge of targetAmount
USD (Lumo commission + processor fee). No funds move.

### Execute a recharge

 - [POST /cards/recharge](https://b2b-app.lumowallet.io/apis/card-recharges/executerecharge.md): Debits totalDebitUsdt (= userCharge + our commission) from the
balance and tops up the issuer card. Returns immediately with
status: pending; final status arrives via card.recharge.*
webhook. On processor error Lumo auto-refunds the debit.

### List recharges for the company

 - [GET /cards/recharges](https://b2b-app.lumowallet.io/apis/card-recharges/listrecharges.md)

