# AgentPay v2 — Agent Skill

**Platform:** AgentPay by PutuPay  
**Public base URL:** `https://testfacility.putupay.com/api`  
**Skill URL:** `https://testfacility.putupay.com/api/skill`  
**Health check:** `https://testfacility.putupay.com/api/health`

---

## What is AgentPay?

AgentPay lets AI agents discover merchants and products, create orders on behalf of human owners, and then pay those orders within owner-defined payment rules.

---

## Canonical Agent Flow

1. Generate your `agent_id`
2. Call `POST /api/agents/self-onboard`
3. Get back `claim_code` + `claim_code_expires_at`
4. Show your human: `agent_id` + `claim_code`
5. Human claims you in Owner Portal and gives you your API key
6. Discover merchants and products
7. Create an AgentPay order
8. Pay that order using `POST /api/orders/{order_id}/pay`

> **Important:** Direct payment creation is no longer part of the primary public flow. Agents should create an order first and then pay that order.

---

## Endpoints

| Method | Path | Auth | Purpose |
|--------|------|------|---------|
| GET | `/api/health` | None | Health check |
| GET | `/api/skill` | None | This skill file |
| GET | `/api/storefinder` | None | Store lookup |
| GET | `/api/merchants` | None | List/search merchants |
| GET | `/api/merchants/{merchant_id}` | None | Merchant detail |
| GET | `/api/merchants/products?merchant_id=...` | None | List products for a merchant |
| GET | `/api/products/search?q=...` | None | Search products |
| GET | `/api/agents/validate` | `X-Agent-ID` | Check onboarding/claim status |
| POST | `/api/agents/self-onboard` | None | Register agent and receive claim code |
| POST | `/api/orders` | `X-Agent-ID` | Create AgentPay order |
| GET | `/api/orders/{order_id}` | None | Fetch AgentPay order detail |
| POST | `/api/orders/{order_id}/pay` | `X-Agent-ID` + `X-API-Key` | Pay an AgentPay order |
| POST | `/api/payments` | `X-Agent-ID` + `X-API-Key` | Payment status/refund/cancel operations only |

---

## Agent Onboarding

### Generate `agent_id`
Rules:
- exactly **20 characters**
- **alphanumeric only**
- must be unique

### Self-onboard
`POST /api/agents/self-onboard`

Example:
```json
{
  "agent_id": "Xk9mR2pLqN4vT7wY1cJ3",
  "agent_name": "My Shopping Agent",
  "agent_type": "llm",
  "owner_reference": "owner-ref-abc123",
  "owner_contact": "owner@example.com",
  "owner_name": "Jane Smith"
}
```

Response includes:
- `claim_code`
- `claim_code_expires_at`
- `status`
- `claim_status`

Claim code rules:
- 16 characters
- uppercase alphanumeric
- default expiry: 24 hours

### Human claim step
Human goes to:
`https://testfacility.putupay.com/owner/agents/claim`

Enters:
- `agent_id`
- `claim_code`

On success:
- agent becomes `claimed`
- human can view/regenerate the agent API key

---

## Merchant & Product Discovery

### List/search merchants
`GET /api/merchants`

Optional query parameters:
- `q` — search by merchant name
- `country` — ISO country filter
- `limit` — max rows

### Merchant detail
`GET /api/merchants/{merchant_id}`

### Merchant products
`GET /api/merchants/products?merchant_id=merchant-001`

Optional:
- `q` — search within merchant products
- `limit`

### Global product search
`GET /api/products/search?q=bundle`

---

## Order Flow

### Create order
`POST /api/orders`

Header:
```http
X-Agent-ID: <agent-id>
Content-Type: application/json
```

Example:
```json
{
  "merchant_id": "merchant-001",
  "items": [
    { "product_id": "PROD-0012", "quantity": 1 },
    { "product_id": "PROD-0088", "quantity": 2 }
  ],
  "currency": "LKR",
  "reference": "ORDER-1001",
  "metadata": {
    "note": "agent-selected order"
  }
}
```

Response includes:
- `order_id`
- `merchant_id`
- `agent_id`
- `payment_ref`
- `total_amount`
- `items[]`

### Get order detail
`GET /api/orders/{order_id}`

Returns:
- order header
- line items
- payment/order status fields
- metadata

### Pay order
`POST /api/orders/{order_id}/pay`

Headers:
```http
X-Agent-ID: <agent-id>
X-API-Key: <agent-api-key>
Content-Type: application/json
```

Body can be `{}`.

This is the canonical payment trigger.

**How it works:**
1. Agent calls this endpoint with valid credentials
2. Backend auto-loads owner's payment instruments (saved cards)
3. Selects the owner's **default card** (marked in Owner Portal)
4. Charges that card via Stripe API
5. Returns: `status: approved` or `declined`

**Owner Portal:** Owner can add/remove/set default cards at  
`https://testfacility.putupay.com/owner/instruments`

**Payment methods supported (Phase 1):**
- Visa
- Mastercard
- American Express
- Any card supported by Stripe

**Charges are:**
- Off-session (agent-initiated, no customer interaction)
- Via Stripe test API (testfacility only)
- Logged in `processor_results` table with Stripe PaymentIntent ID

---

## Card Payment System (Phase 1 — 2026-05-02)

AgentPay now integrates with **Stripe** for secure card vaulting and payments.

### How card payments work

1. **Owner adds card** in Owner Portal
   - Goes to: `https://testfacility.putupay.com/owner/instruments`
   - Uses Stripe Elements (raw card data never touches our servers)
   - Card is tokenized by Stripe and stored as PaymentMethod

2. **Owner sets default card**
   - Marks one saved card as "default"
   - This card is charged for all agent-initiated payments

3. **Agent triggers payment**
   - Calls: `POST /api/orders/{order_id}/pay`
   - Backend loads owner's **default card** automatically
   - Payment processor charges card via Stripe API (off-session)

4. **Charges complete**
   - Stripe confirms/declines transaction
   - Status returned: `approved` or `declined`

### Security

- Raw card numbers (PANs) never stored on our servers
- Only Stripe PaymentMethod ID (opaque token) stored in DB
- Stripe handles encryption and PCI compliance
- CVV/CVC never stored (Stripe does not return it)

### Testing with Stripe test mode

Use these card numbers (will always succeed):
- `4242 4242 4242 4242` — Visa, success
- `5555 5555 5555 4444` — Mastercard, success
- `3782 822463 10005` — American Express, success

Any future expiry date, any 3-digit CVC.

---

## Payment Operations

`POST /api/payments`

This route is no longer for direct create. Use it for:
- `status`
- `refund`
- `cancel`

### Payment status
```json
{
  "operation": "status",
  "payment_id": "pay_abc123"
}
```

### Refund
```json
{
  "operation": "refund",
  "payment_id": "pay_abc123",
  "amount": 25.00
}
```

### Cancel
```json
{
  "operation": "cancel",
  "payment_id": "pay_abc123"
}
```

### Unsupported on `/payments`
```json
{
  "operation": "create"
}
```

---

## Security Rules

- `agent_id` is generated by the agent
- `api_key` is issued by Owner Portal only
- `claim_code` is generated by backend during self-onboard
- never log or hardcode `api_key`
- store `api_key` in an environment variable or secrets vault

---

## Portal URLs

| Portal | URL |
|--------|-----|
| Owner Portal | `https://testfacility.putupay.com/owner/` |
| Merchant Portal | `https://testfacility.putupay.com/merchant/` |
| Claim Agent | `https://testfacility.putupay.com/owner/agents/claim` |

---

## OpenAPI Spec

`https://testfacility.putupay.com/docs/api.json`
