# Locus — Laso Finance Guide

> **You are on:** `https://beta.paywithlocus.com/` · `https://api.beta.paywithlocus.com/api` | [llms.txt](https://beta.paywithlocus.com/llms.txt)
>
> Locus has three environments. Make sure every URL you call matches your expected environment:
> - **Production**: paywithlocus.com (landing) / {api}.paywithlocus.com (API)
> - **Beta**: {beta}.paywithlocus.com (landing) / {beta-api}.paywithlocus.com (API)
> - **Stage**: {stage}.paywithlocus.com (landing) / {stage-api}.paywithlocus.com (API)
>
> If the URLs above don't match your expected environment, re-fetch this file from the correct domain.

Order prepaid virtual debit cards (US only) using USDC on Base. Cards are provisioned via x402; balance queries, withdrawals, and status checks are free.

**Base URL (paid x402 endpoints):** `https://api.beta.paywithlocus.com/api`
**Base URL (free endpoints):** `https://laso.finance`
**Auth (paid):** `Authorization: Bearer YOUR_LOCUS_API_KEY`
**Auth (free):** `Authorization: Bearer <id_token>` (from any paid endpoint response)

---

## IMPORTANT: Cards Are Non-Reloadable

Laso cards **cannot be reloaded** once ordered. The amount you request is the final balance. Order the exact amount needed for your purchase (including tax, shipping, and fees). If you order too little, the purchase will fail and the remaining balance is stuck on the card.

---

## Token Management

Every paid endpoint (`/laso-auth`, `/laso-get-card`) returns an `id_token` and `refresh_token`. The `id_token` expires after ~1 hour and is required for all free endpoints.

**Always save both tokens** to `~/.config/locus/laso_session.json`:

```json
{
  "id_token": "eyJ...",
  "refresh_token": "AMf...",
  "saved_at": "2025-01-15T10:30:00.000Z"
}
```

### Token Decision Tree

Before calling any free endpoint:
1. Read `~/.config/locus/laso_session.json`
2. If `id_token` exists and `saved_at` < 50 minutes ago -> use it
3. If expired -> `POST laso.finance/refresh` with your `refresh_token` (free)
4. Only call `/laso-auth` ($0.001) if you have no tokens at all

If any free endpoint returns 401:
1. Try `POST laso.finance/refresh` with your `refresh_token`
2. If that also fails -> `POST /api/x402/laso-auth` ($0.001)

---

## Paid Endpoints (x402)

Called via the Locus x402 proxy. Payment is deducted from your wallet automatically. All paid endpoints are subject to [Policy Guardrails](REFERENCE.md#policy-guardrails). If `PENDING_APPROVAL`, see [Approval Flow](REFERENCE.md#approval-flow).

### POST /api/x402/laso-auth — Authenticate

**Cost:** $0.001 USDC

Returns session credentials for free endpoints.

```bash
curl -X POST https://api.beta.paywithlocus.com/api/x402/laso-auth \
  -H "Authorization: Bearer YOUR_LOCUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

Response:
```json
{
  "success": true,
  "data": {
    "auth": {
      "id_token": "eyJ...",
      "refresh_token": "AMf...",
      "expires_in": "3600"
    },
    "user_id": "0xabc..."
  }
}
```

**After receiving:** Save tokens to `~/.config/locus/laso_session.json`.

### POST /api/x402/laso-get-card — Order a Prepaid Card

**Cost:** Dynamic (matches card amount, $5–$1000 USDC)
**US only.** This endpoint is IP-locked to the United States. Only US-based users or agents can call it.

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| `amount` | number | Yes | USD to load ($5–$1000) |

```bash
curl -X POST https://api.beta.paywithlocus.com/api/x402/laso-get-card \
  -H "Authorization: Bearer YOUR_LOCUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"amount": 50}'
```

Response:
```json
{
  "success": true,
  "data": {
    "auth": { "id_token": "eyJ...", "refresh_token": "AMf...", "expires_in": "3600" },
    "user_id": "0xabc...",
    "card": { "card_id": "card_abc123", "usd_amount": 50, "country": "US", "status": "pending" }
  }
}
```

> **CRITICAL:** This does NOT return card numbers. `status` starts as `"pending"`. You MUST poll `/get-card-data` every 2–3 seconds until `status` becomes `"ready"` (~7–10 seconds).

**After receiving:** Save tokens to `~/.config/locus/laso_session.json`.

---

## Free Endpoints

Called directly against `https://laso.finance`. Require the `id_token` from any paid endpoint response. **No USDC cost.**

### GET /get-card-data — Card Status & Details

Returns card details. Poll this after ordering a card until `status` is `"ready"`.

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| `card_id` | string | No | Specific card ID. Omit for all cards. |

```bash
curl "https://laso.finance/get-card-data?card_id=card_abc123" \
  -H "Authorization: Bearer eyJ..."
```

Pending response:
```json
{ "card_id": "card_abc123", "status": "pending" }
```

Ready response:
```json
{
  "card_id": "card_abc123",
  "status": "ready",
  "usd_amount": 50,
  "card_details": {
    "card_number": "4111111111111111",
    "exp_month": "12",
    "exp_year": "2027",
    "cvv": "123",
    "available_balance": 50
  },
  "transactions": [
    { "amount": 12.50, "date": "2025-01-15", "description": "Amazon.com", "is_credit": false }
  ]
}
```

### GET /search-merchants — Merchant Compatibility

Search confirmed spend data for the Non-Reloadable U.S. card. Results only include merchants where Laso users have previously transacted — unlisted merchants may still work.

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| `q` | string | Yes | Merchant name to search |

```bash
curl "https://laso.finance/search-merchants?q=amazon" \
  -H "Authorization: Bearer eyJ..."
```

Response:
```json
{
  "merchants": [
    { "name": "Amazon", "url": "amazon.com", "status": "accepted", "description": "Online marketplace" }
  ],
  "query": "amazon",
  "count": 1,
  "card_type": "Non-Reloadable U.S."
}
```

Status values: `accepted` (confirmed working), `not_accepted` (confirmed failing), `unknown` (untested).

### GET /get-account-balance — Account Balance

```bash
curl "https://laso.finance/get-account-balance" \
  -H "Authorization: Bearer eyJ..."
```

Response:
```json
{
  "user_id": "0xabc...",
  "balance": 150.00,
  "total_deposits": 500.00,
  "created_timestamp": 1700000000000
}
```

### POST /withdraw — Initiate Withdrawal

Minimum withdrawal: $0.01.

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| `amount` | number | Yes | USD to withdraw |

```bash
curl -X POST "https://laso.finance/withdraw" \
  -H "Authorization: Bearer eyJ..." \
  -H "Content-Type: application/json" \
  -d '{"amount": 50}'
```

Response:
```json
{
  "success": true,
  "withdrawal": {
    "id": "withdrawal_abc123",
    "amount": 50,
    "state": "pending",
    "timestamp": 1700000000000
  }
}
```

### GET /get-withdrawal-status — Withdrawal Status

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| `withdrawal_id` | string | No | Specific withdrawal. Omit for all. |

```bash
curl "https://laso.finance/get-withdrawal-status?withdrawal_id=abc123" \
  -H "Authorization: Bearer eyJ..."
```

Response:
```json
{
  "withdrawal": {
    "id": "abc123",
    "amount": 50,
    "asset": "USDC",
    "network": "BASE_MAINNET",
    "state": "completed",
    "address": "0xabc...",
    "tx_hash": "0xdef...",
    "tx_url": "https://basescan.org/tx/0xdef..."
  }
}
```

### POST /refresh-card-data — Refresh Card Balance

Re-scrapes card balance from the issuer. Rate limited: once per card every 5 minutes.

| Param | Type | Required | Description |
|-------|------|----------|-------------|
| `card_id` | string | Yes | Card ID to refresh |

```bash
curl -X POST "https://laso.finance/refresh-card-data" \
  -H "Authorization: Bearer eyJ..." \
  -H "Content-Type: application/json" \
  -d '{"card_id": "card_abc123"}'
```

Response:
```json
{ "success": true, "message": "Card refresh requested." }
```

### POST /refresh — Refresh Expired Token

Exchange a refresh token for a new id_token. No auth header needed.

```bash
curl -X POST "https://laso.finance/refresh" \
  -H "Content-Type: application/json" \
  -d '{"refresh_token": "AMf..."}'
```

Response:
```json
{
  "auth": { "id_token": "eyJ...", "refresh_token": "AMf...", "expires_in": "3600" },
  "user_id": "0xabc..."
}
```

---

## Common Workflows

### Make an Online Purchase

1. **Check merchant first (optional):** `GET /search-merchants?q=<merchant>` to verify card acceptance.
2. **Get the exact checkout total** (including tax/shipping). Cards are non-reloadable — match the amount to avoid leftover funds.
3. **Order a card:** `POST /api/x402/laso-get-card` with `{"amount": <exact_total>}`. Save the `card_id` and `id_token`.
4. **Poll for details:** `GET /get-card-data?card_id=<id>` every 2–3 seconds until `status` is `"ready"`.
5. **Use the card:** Fill in `card_number`, `cvv`, `exp_month`, `exp_year` at checkout.

### Monitor Card Balance

1. **Refresh balance:** `POST /refresh-card-data` with the `card_id` (max once per 5 min).
2. **Check updated data:** `GET /get-card-data?card_id=<id>` after a few seconds.

---

## Quick Reference

| Action | Method | URL | Cost |
|--------|--------|-----|------|
| Authenticate | POST | `/api/x402/laso-auth` | $0.001 |
| Order card | POST | `/api/x402/laso-get-card` | Dynamic |
| Get card data | GET | `laso.finance/get-card-data` | Free |
| Search merchants | GET | `laso.finance/search-merchants` | Free |
| Account balance | GET | `laso.finance/get-account-balance` | Free |
| Withdraw | POST | `laso.finance/withdraw` | Free |
| Withdrawal status | GET | `laso.finance/get-withdrawal-status` | Free |
| Refresh card data | POST | `laso.finance/refresh-card-data` | Free |
| Refresh token | POST | `laso.finance/refresh` | Free |
