# API Reference

## Authentication

toani Facilitate supports two authentication methods:

### API Key

Pass `apiKey` in the request body. Used by MCP Gateway and external integrations.

```json
{
  "apiKey": "your-api-key-here",
  "walletAddress": "0x..."
}
```

### Session Authentication (JWT)

Used by Dashboard frontend. Credentials are passed as:

* **Cookie**: `agentry_session` (automatically set after login)
* **Header**: `Authorization: Bearer <token>`

Session-authenticated endpoints do not require `apiKey` in the request body.

***

## Business Codes

All API responses use a unified `code` field indicating result status. Business codes are decoupled from HTTP status codes.

| Code       | Constant                 | Meaning                | HTTP |
| ---------- | ------------------------ | ---------------------- | ---- |
| `80000000` | `SUCCESS`                | Success                | 200  |
| `80000001` | `SYSTEM_ERROR`           | System error           | 500  |
| `80000002` | `PARAM_ERROR`            | Parameter error        | 400  |
| `80000003` | `REPEATED_SUBMIT`        | Duplicate submission   | 409  |
| `80000004` | `UNAUTHORIZED_OPERATION` | Unauthorized operation | 403  |
| `80000011` | `BUSINESS_NOT_FOUND`     | Resource not found     | 404  |
| `80000012` | `INVALID_PARAM`          | Invalid parameter      | 400  |
| `80000019` | `AUTHENTICATION_FAILED`  | Authentication failed  | 401  |
| `80000040` | `API_KEY_INVALID`        | API Key invalid        | 401  |
| `80000044` | `ACCESS_TOKEN_REQUIRED`  | Access token required  | 401  |
| `80000053` | `MANDATE_NOT_FOUND`      | Mandate not found      | 404  |
| `80000054` | `MANDATE_EXPIRED`        | Mandate expired        | 400  |
| `80000055` | `MANDATE_EXHAUSTED`      | Mandate exhausted      | 400  |
| `80000056` | `INTENT_PER_TX_EXCEEDED` | Per-tx limit exceeded  | 400  |
| `80000057` | `INTENT_BUDGET_EXCEEDED` | Budget exceeded        | 400  |

***

## API Endpoints

### 1. Get Account Public Key

Retrieve the public key associated with a wallet address. Used by Gateway for AP2 merchant signature verification.

```
POST /api/accounts/public-key
```

**Authentication**: API Key (used by Facilitate)

**Request Body**:

```json
{
  "apiKey": "your-api-key",
  "walletAddress": "0x1234567890abcdef1234567890abcdef12345678"
}
```

**Success Response (200)**:

```json
{
  "code": 80000000,
  "msg": "success",
  "data": {
    "walletAddress": "0x1234567890abcdef1234567890abcdef12345678",
    "publicKey": "0x04a1b2c3d4e5f6..."
  }
}
```

The `publicKey` is in uncompressed format (starts with `0x04`).

**Error Response - Account Not Found (404)**:

```json
{
  "code": 80000011,
  "msg": "Account not found",
  "data": null
}
```

***

### 2. Get Payer Intent VC

Retrieve the payer's current active Intent Mandate and its verifiable credential. Called by Gateway during AP2 verification.

```
POST /api/mandates/intent/vc
```

**Authentication**: API Key

**Request Body**:

```json
{
  "apiKey": "your-api-key",
  "payerWalletAddress": "0x1234567890abcdef1234567890abcdef12345678"
}
```

**Success Response - Mandate Found (200)**:

```json
{
  "code": 80000000,
  "msg": "success",
  "data": {
    "found": true,
    "mandate": {
      "id": "cm5abc123def456",
      "perTxLimit": "5.00",
      "totalBudget": "50.00",
      "usedAmount": "12.30",
      "transactionCount": 8,
      "expiresAt": "2026-04-30T00:00:00.000Z",
      "status": "active"
    },
    "remainingBudget": "37.70",
    "usagePercent": 24.6,
    "vcJson": {
      "@context": ["https://www.w3.org/2018/credentials/v1"],
      "type": ["VerifiableCredential", "AP2IntentMandate"],
      "issuer": "did:ethr:eip155:<chainId>:0x1234...5678",
      "credentialSubject": {
        "mandateId": "cm5abc123def456",
        "accountId": "acc123",
        "payer": {
          "address": "0x1234...5678",
          "agentryId": "0x1234...5678"
        },
        "budgetConstraints": {
          "totalBudget": { "amount": "50.00", "currency": "USDC" },
          "perTransactionLimit": { "amount": "5.00", "currency": "USDC" }
        },
        "title": "Daily research budget"
      },
      "proof": {
        "type": "EthereumEip712Signature2021",
        "verificationMethod": "did:ethr:eip155:<chainId>:0x1234...5678#controller",
        "proofValue": "0xabcdef..."
      }
    }
  }
}
```

**Error Response - Mandate Not Found (404)**:

```json
{
  "code": 80000053,
  "msg": "Mandate validation failed: MANDATE_NOT_FOUND",
  "data": {
    "found": false,
    "mandate": null,
    "remainingBudget": null,
    "usagePercent": null,
    "vcJson": null
  }
}
```

***

### 3. KYC Status Check

Query KYC verification status for a wallet address (via zkMe).

```
POST /api/kyc/check
```

**Authentication**: API Key

**Request Body**:

```json
{
  "apiKey": "your-api-key",
  "walletAddress": "0x1234567890abcdef1234567890abcdef12345678"
}
```

**Success Response (200)**:

```json
{
  "code": 80000000,
  "msg": "success",
  "data": {
    "walletAddress": "0x1234567890abcdef1234567890abcdef12345678",
    "kycCompleted": true,
    "kycStatus": "approved"
  }
}
```

**kycStatus Possible Values**:

| Status     | Description                             |
| ---------- | --------------------------------------- |
| `pending`  | Verification submitted but not complete |
| `approved` | Identity verified                       |
| `rejected` | Verification failed                     |

***

### 4. KYT Risk Check

Perform real-time risk scanning on a wallet address (via MistTrack).

```
POST /api/compliance/kyt/check
```

**Authentication**: API Key

**Request Body**:

```json
{
  "apiKey": "your-api-key",
  "walletAddress": "0x1234567890abcdef1234567890abcdef12345678",
  "chain": "ETH"
}
```

The `chain` parameter is optional; default is `ETH`.

**Success Response (200)**:

```json
{
  "code": 80000000,
  "msg": "success",
  "data": {
    "address": "0x1234567890abcdef1234567890abcdef12345678",
    "chain": "ETH",
    "decision": "pass",
    "riskLevel": "low",
    "riskScore": 12
  }
}
```

**Response Fields**:

| Field       | Type                                         | Description                      |
| ----------- | -------------------------------------------- | -------------------------------- |
| `decision`  | `"pass"` or `"reject"`                       | Whether wallet passed risk check |
| `riskLevel` | `"low"`, `"medium"`, `"high"`, or `"severe"` | Risk level                       |
| `riskScore` | number                                       | Risk score (lower is safer)      |

***

### 5. Create Transaction Record

Record transaction log after settlement completes. Called asynchronously by Gateway.

```
POST /api/transactions
```

**Authentication**: API Key

**Request Body**:

```json
{
  "apiKey": "your-api-key",
  "serviceName": "weather-service",
  "serviceResult": "pass",
  "chain": "base",
  "txHash": "0xabc123...",
  "amount": "0.10",
  "tokenSymbol": "USDC",
  "payer": "0x1234...5678",
  "payee": "0xabcd...ef01",
  "intentMandateId": "cm5abc123def456",
  "payerKycResult": 1,
  "payerKytResult": 1,
  "payerKytRiskLevel": 0,
  "payeeKycResult": 1,
  "payeeKytResult": 1,
  "payeeKytRiskLevel": 0,
  "metadata": {
    "toolName": "get_weather",
    "requestId": "req-abc-123"
  }
}
```

**Required Fields**: `apiKey`, `serviceName`, `serviceResult`, `chain`

**Optional Fields**: `txHash`, `amount`, `tokenSymbol`, `payer`, `payee`, `intentMandateId`, `payerKycResult`, `payerKytResult`, `payerKytRiskLevel`, `payeeKycResult`, `payeeKytResult`, `payeeKytRiskLevel`, `metadata`

KYC/KYT result fields use integers: `1` = pass, `0` = fail/unchecked. Risk levels: `0` = low, `1` = moderate, `2` = high, `3` = severe.

**Success Response (201)**:

```json
{
  "code": 80000000,
  "msg": "success",
  "data": {
    "id": 42
  }
}
```

If `serviceResult === "pass"` and `intentMandateId` is present, the server automatically calls `updateMandateUsage` to increment usage.

***

### 6. Query Transaction Records

Query transaction history for current account with filtering and pagination.

```
GET /api/transactions
```

**Authentication**: Session (JWT Cookie or Bearer Token)

**Query Parameters**:

| Parameter       | Type   | Required | Description                                                                      |
| --------------- | ------ | -------- | -------------------------------------------------------------------------------- |
| `scope`         | string | Yes      | Filter dimension: `myPayer` (I am payer), `myPayee` (I am payee), `myAll` (both) |
| `page`          | number | No       | Page number (default 1)                                                          |
| `limit`         | number | No       | Records per page (default 20)                                                    |
| `chain`         | string | No       | Filter by chain                                                                  |
| `txHash`        | string | No       | Filter by transaction hash                                                       |
| `serviceName`   | string | No       | Filter by service name                                                           |
| `serviceResult` | string | No       | Filter by result (pass/reject/error)                                             |
| `startDate`     | string | No       | Start date (ISO 8601)                                                            |
| `endDate`       | string | No       | End date (ISO 8601)                                                              |

**Request Example**:

```
GET /api/transactions?scope=myPayer&page=1&limit=10&chain=base
```

**Success Response (200)**:

```json
{
  "code": 80000000,
  "msg": "success",
  "data": {
    "data": [
      {
        "id": 42,
        "serviceName": "weather-service",
        "serviceResult": "pass",
        "chain": "base",
        "txHash": "0xabc123...",
        "amount": "0.10",
        "tokenSymbol": "USDC",
        "payer": "0x1234...5678",
        "payee": "0xabcd...ef01",
        "payerKycResult": 1,
        "payerKytResult": 1,
        "payerKytRiskLevel": 0,
        "payeeKycResult": 1,
        "payeeKytResult": 1,
        "payeeKytRiskLevel": 0,
        "createdAt": "2026-03-29T14:30:00.000Z"
      }
    ],
    "total": 47,
    "page": 1,
    "limit": 10
  }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.toani.ai/toani-facilitate/getting-started/api-reference.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
