# REST API reference

Base URL: `https://api-public.pay.nicky.me`

All endpoints below are **anonymous** — no API key, no Authorization header. The agent should
not send any auth header on these calls.

## Get payment request

```
GET /api/agents/payment-request?shortId={shortId}
```

| Query param | Type   | Notes                                          |
| ----------- | ------ | ---------------------------------------------- |
| `shortId`   | string | 6-character short ID, or a `nick` (username)   |

Example:

```bash
curl 'https://api-public.pay.nicky.me/api/agents/payment-request?shortId=ABCDEF'
```

Response (shape — fields may evolve, see OpenAPI spec for the canonical schema):

```json
{
  "shortId": "ABCDEF",
  "receiverUser": "alice",
  "receiverDisplayName": "Alice Example",
  "totalAmount": 50.0,
  "remainingAmount": 50.0,
  "currency": "USDT",
  "nativeAssetId": "USDT_TRC20",
  "acceptedAssets": [
    {
      "assetId": "USDT_TRC20",
      "assetDisplayName": "USDT (Tron, TRC-20)",
      "amount": 50.0,
      "isNative": true
    },
    {
      "assetId": "ETH.USDT",
      "assetDisplayName": "USDT (Ethereum, ERC-20)",
      "amount": 50.0,
      "isNative": false,
      "conversionRate": 1.0
    }
  ],
  "requiredPayerFields": ["name", "email"],
  "status": "Pending",
  "expiresAt": "2026-06-02T13:55:00Z"
}
```

Notes:

- `remainingAmount` may be less than `totalAmount` for partially-paid requests.
- `acceptedAssets[i].amount` is the *converted* amount the payer should send if they choose
  that asset. Use that value, not the requested amount, when paying in a non-native asset.
- `status` is `Pending`, `Confirmed`, or `Expired` from the receiver's perspective.

## Start payment

```
POST /api/agents/payment/start
Content-Type: application/json
```

Body:

```json
{
  "paymentRequestShortId": "ABCDEF",
  "assetId": "USDT_TRC20",
  "payerName": "John Doe",
  "payerEmail": "john@example.com"
}
```

| Field                    | Required | Notes                                                                       |
| ------------------------ | -------- | --------------------------------------------------------------------------- |
| `paymentRequestShortId`  | yes      | The `shortId` from `get-payment-request`                                    |
| `assetId`                | yes      | One of the IDs from `acceptedAssets[].assetId`                              |
| `payerName`              | yes      | Receiver-visible identifier                                                 |
| `payerEmail`             | yes      | Receiver-visible contact                                                    |

Response:

```json
{
  "paymentAttemptId": "X7K2P1",
  "walletAddress": "TX...abc",
  "amount": 50.0,
  "assetId": "USDT_TRC20",
  "expiresAt": "2026-06-02T13:55:00Z",
  "status": "Pending"
}
```

Idempotency: calling `start-payment` again with the same `paymentRequestShortId`,
`assetId`, `payerEmail`, and (typically) the same payer identity returns the *same*
`paymentAttemptId` and `walletAddress`. Safe to call if the user lost the previous attempt.

## Report transaction

```
POST /api/agents/payment/report-transaction
Content-Type: application/json
```

Body:

```json
{
  "paymentAttemptId": "X7K2P1",
  "transactionHash": "0xabc123..."
}
```

| Field             | Required | Notes                                                                            |
| ----------------- | -------- | -------------------------------------------------------------------------------- |
| `paymentAttemptId`| yes      | Returned by `start-payment`                                                      |
| `transactionHash` | yes      | The full on-chain tx hash. Nicky validates it on-chain and starts confirmation.  |

This call is **strongly recommended** as soon as the user (or the agent's wallet) broadcasts
the transaction. It short-circuits Nicky's deposit-discovery polling and jumps the pipeline
to blockchain validation.

## Payment progress

```
GET /api/agents/payment/progress?paymentAttemptId={id}
```

Response:

```json
{
  "paymentAttemptId": "X7K2P1",
  "status": "Received",
  "isPaid": false,
  "confirmations": 4,
  "amount": 50.0,
  "assetId": "USDT_TRC20",
  "transactionHash": "0xabc123...",
  "lastUpdatedAt": "2026-06-02T13:40:00Z"
}
```

| Field              | Meaning                                                                              |
| ------------------ | ------------------------------------------------------------------------------------ |
| `status`           | `Pending` / `Received` / `Confirmed` / `Expired` / `Error`                           |
| `isPaid`           | True once the request is fully settled                                              |
| `confirmations`    | Current on-chain confirmation count (Nicky needs 10+)                                |
| `errorReason`      | Present when `status == "Error"` (e.g. `MaxCheckCount`, `Blacklisted`, `InvalidQuote`) |
| `transactionHash`  | The hash Nicky is tracking (populated after `report-transaction` or auto-discovery)   |

**Poll cadence:** every 15–30 seconds is appropriate. Avoid polling faster than 10 s — the
pipeline itself runs on 30–60 s intervals and faster polling does not produce fresher data.

## Error responses

The API returns standard HTTP status codes:

| Status | Meaning                                                                |
| ------ | ---------------------------------------------------------------------- |
| 200/201 | Success                                                               |
| 400    | Malformed request (e.g. unknown `assetId` for the request)             |
| 404    | Unknown `shortId` or `paymentAttemptId`                                |
| 409    | Conflict — request already paid, expired, or payer info inconsistent   |
| 429    | Rate limited (per-IP / per-token)                                      |
| 5xx    | Server error — back off and retry with jitter                          |

The agent should treat any 5xx as transient and retry the call once after 2–3 s before
surfacing the error to the user.
