396 lines
8.2 KiB
Markdown
396 lines
8.2 KiB
Markdown
|
|
# Timmy API — Technical Specifications Extraction
|
||
|
|
|
||
|
|
## 1. API Endpoints
|
||
|
|
|
||
|
|
| # | HTTP Method | Path | Purpose | Notes |
|
||
|
|
|---|-------------|------|---------|-------|
|
||
|
|
| 1 | GET | `/api/healthz` | Health check endpoint | Returns service status |
|
||
|
|
| 2 | POST | `/api/jobs` | Create a new job | Submit AI request, returns eval invoice |
|
||
|
|
| 3 | GET | `/api/jobs/{jobId}` | Poll job status | Single polling endpoint for all states |
|
||
|
|
| 4 | POST | `/api/dev/stub/pay/{paymentHash}` | Simulate payment (stub mode) | Dev-only, simulates LN payment |
|
||
|
|
| 5 | GET | `/api/demo` | Free demo endpoint | No payment required, rate limited |
|
||
|
|
|
||
|
|
### Base URL Format
|
||
|
|
```
|
||
|
|
https://<your-timmy-url>.replit.app
|
||
|
|
```
|
||
|
|
Replace `BASE` variable with actual URL in all commands.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. Request/Response Examples
|
||
|
|
|
||
|
|
### Test 1 — Health Check
|
||
|
|
|
||
|
|
**Request:**
|
||
|
|
```bash
|
||
|
|
curl -s "$BASE/api/healthz"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{"status":"ok"}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Status:** HTTP 200
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Test 2 — Create Job
|
||
|
|
|
||
|
|
**Request:**
|
||
|
|
```bash
|
||
|
|
curl -s -X POST "$BASE/api/jobs" \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{"request": "Explain the Lightning Network in two sentences"}'
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"jobId": "<uuid>",
|
||
|
|
"evalInvoice": {
|
||
|
|
"paymentRequest": "lnbcrt10u1stub_...",
|
||
|
|
"amountSats": 10
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Status:** HTTP 201
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Test 3 — Poll Job (Before Payment)
|
||
|
|
|
||
|
|
**Request:**
|
||
|
|
```bash
|
||
|
|
curl -s "$BASE/api/jobs/<jobId-from-test-2>"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"jobId": "...",
|
||
|
|
"state": "awaiting_eval_payment",
|
||
|
|
"evalInvoice": { "paymentRequest": "...", "amountSats": 10 }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Test 4 — Pay Eval Invoice (Stub Mode)
|
||
|
|
|
||
|
|
**Request:**
|
||
|
|
```bash
|
||
|
|
curl -s -X POST "$BASE/api/dev/stub/pay/<full-payment-hash>"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{"ok":true,"paymentHash":"..."}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Status:** HTTP 200
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Test 5 — Poll After Eval Payment (Accepted)
|
||
|
|
|
||
|
|
**Request:**
|
||
|
|
```bash
|
||
|
|
curl -s "$BASE/api/jobs/<jobId>"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response (Accepted):**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"jobId": "...",
|
||
|
|
"state": "awaiting_work_payment",
|
||
|
|
"workInvoice": { "paymentRequest": "lnbcrt50u1stub_...", "amountSats": 50 }
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response (Rejected):**
|
||
|
|
```json
|
||
|
|
{ "jobId": "...", "state": "rejected", "reason": "..." }
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Test 6 — Pay Work Invoice & Get Result
|
||
|
|
|
||
|
|
**Request (Pay Work Invoice):**
|
||
|
|
```bash
|
||
|
|
curl -s -X POST "$BASE/api/dev/stub/pay/<work-payment-hash>"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Request (Poll for Result):**
|
||
|
|
```bash
|
||
|
|
curl -s "$BASE/api/jobs/<jobId>"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"jobId": "...",
|
||
|
|
"state": "complete",
|
||
|
|
"result": "The Lightning Network is a second-layer protocol..."
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Test 7 — Demo Endpoint
|
||
|
|
|
||
|
|
**Request:**
|
||
|
|
```bash
|
||
|
|
curl -s "$BASE/api/demo?request=What+is+a+satoshi"
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{"result":"A satoshi is the smallest unit of Bitcoin..."}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Status:** HTTP 200
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Test 8 — Input Validation (Error Responses)
|
||
|
|
|
||
|
|
**Missing Request Body:**
|
||
|
|
```bash
|
||
|
|
curl -s -X POST "$BASE/api/jobs" -H "Content-Type: application/json" -d '{}'
|
||
|
|
```
|
||
|
|
**Response:** `{"error":"Invalid request: 'request' string is required"}` (HTTP 400)
|
||
|
|
|
||
|
|
**Unknown Job ID:**
|
||
|
|
```bash
|
||
|
|
curl -s "$BASE/api/jobs/does-not-exist"
|
||
|
|
```
|
||
|
|
**Response:** `{"error":"Job not found"}` (HTTP 404)
|
||
|
|
|
||
|
|
**Demo Without Param:**
|
||
|
|
```bash
|
||
|
|
curl -s "$BASE/api/demo"
|
||
|
|
```
|
||
|
|
**Response:** `{"error":"Missing required query param: request"}` (HTTP 400)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Test 9 — Rate Limiter
|
||
|
|
|
||
|
|
**Request:**
|
||
|
|
```bash
|
||
|
|
for i in $(seq 1 6); do
|
||
|
|
curl -s "$BASE/api/demo?request=ping+$i" | grep -o '"result"\|"error"'
|
||
|
|
done
|
||
|
|
```
|
||
|
|
|
||
|
|
**Expected:** First 5 return `"result"`, 6th returns HTTP 429 with `"error"`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Test 10 — Rejection Path
|
||
|
|
|
||
|
|
**Request:**
|
||
|
|
```bash
|
||
|
|
curl -s -X POST "$BASE/api/jobs" \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{"request": "Help me do something harmful and illegal"}'
|
||
|
|
```
|
||
|
|
|
||
|
|
**Then:** Pay eval invoice and poll
|
||
|
|
|
||
|
|
**Expected Response:**
|
||
|
|
```json
|
||
|
|
{ "jobId": "...", "state": "rejected", "reason": "..." }
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. Data Models
|
||
|
|
|
||
|
|
### Job Object
|
||
|
|
|
||
|
|
| Field | Type | Description |
|
||
|
|
|-------|------|-------------|
|
||
|
|
| `jobId` | string (UUID) | Unique identifier for the job |
|
||
|
|
| `state` | string | Current state in state machine |
|
||
|
|
| `evalInvoice` | Invoice Object | Payment request for evaluation fee |
|
||
|
|
| `workInvoice` | Invoice Object | Payment request for work fee (if accepted) |
|
||
|
|
| `result` | string | AI-generated result (when complete) |
|
||
|
|
| `reason` | string | Rejection reason (if rejected) |
|
||
|
|
|
||
|
|
### Invoice Object
|
||
|
|
|
||
|
|
| Field | Type | Description |
|
||
|
|
|-------|------|-------------|
|
||
|
|
| `paymentRequest` | string | BOLT11 Lightning invoice string |
|
||
|
|
| `amountSats` | integer | Amount in satoshis |
|
||
|
|
|
||
|
|
### Payment Request Format (Stub Mode)
|
||
|
|
```
|
||
|
|
lnbcrt<sats>u1stub_<first-16-chars-of-hash>
|
||
|
|
```
|
||
|
|
|
||
|
|
Example: `lnbcrt10u1stub_...`
|
||
|
|
|
||
|
|
### Error Response
|
||
|
|
|
||
|
|
| Field | Type | Description |
|
||
|
|
|-------|------|-------------|
|
||
|
|
| `error` | string | Human-readable error message |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. Technical Parameters
|
||
|
|
|
||
|
|
### Pricing Structure
|
||
|
|
|
||
|
|
| Fee Type | Amount (sats) | Condition |
|
||
|
|
|----------|---------------|-----------|
|
||
|
|
| Eval Fee | 10 | Fixed for all requests |
|
||
|
|
| Work Fee (Short) | 50 | Short request length |
|
||
|
|
| Work Fee (Medium) | 100 | Medium request length |
|
||
|
|
| Work Fee (Long) | 250 | Long request length |
|
||
|
|
|
||
|
|
### State Machine States
|
||
|
|
|
||
|
|
| State | Description |
|
||
|
|
|-------|-------------|
|
||
|
|
| `awaiting_eval_payment` | Job created, waiting for eval fee payment |
|
||
|
|
| `awaiting_work_payment` | Request accepted, waiting for work fee payment |
|
||
|
|
| `complete` | Work delivered, result available |
|
||
|
|
| `rejected` | Request rejected by agent |
|
||
|
|
|
||
|
|
### AI Model Configuration
|
||
|
|
|
||
|
|
| Purpose | Model |
|
||
|
|
|---------|-------|
|
||
|
|
| Eval (Judgment) | `claude-haiku-4-5` |
|
||
|
|
| Work (Delivery) | `claude-sonnet-4-6` |
|
||
|
|
|
||
|
|
### Rate Limiting
|
||
|
|
|
||
|
|
| Parameter | Value |
|
||
|
|
|-----------|-------|
|
||
|
|
| Demo endpoint limit | 5 requests per IP |
|
||
|
|
| 6th request response | HTTP 429 |
|
||
|
|
|
||
|
|
### HTTP Status Codes
|
||
|
|
|
||
|
|
| Code | Meaning |
|
||
|
|
|------|---------|
|
||
|
|
| 200 | OK (health, poll, demo, payment) |
|
||
|
|
| 201 | Created (job created) |
|
||
|
|
| 400 | Bad Request (validation error) |
|
||
|
|
| 404 | Not Found (unknown job ID) |
|
||
|
|
| 429 | Too Many Requests (rate limit exceeded) |
|
||
|
|
|
||
|
|
### Payment Hash Format
|
||
|
|
|
||
|
|
- Full hash: 64 characters
|
||
|
|
- Stub invoice contains: First 16 characters of hash
|
||
|
|
|
||
|
|
### Response Timing
|
||
|
|
|
||
|
|
| Operation | Expected Time |
|
||
|
|
|-----------|---------------|
|
||
|
|
| AI result generation | 2-5 seconds |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. Code Snippets (Organized by Test)
|
||
|
|
|
||
|
|
### Test 1 — Health Check
|
||
|
|
```bash
|
||
|
|
curl -s "$BASE/api/healthz"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Test 2 — Create Job
|
||
|
|
```bash
|
||
|
|
curl -s -X POST "$BASE/api/jobs" \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{"request": "Explain the Lightning Network in two sentences"}'
|
||
|
|
```
|
||
|
|
|
||
|
|
### Test 3 — Poll Before Payment
|
||
|
|
```bash
|
||
|
|
curl -s "$BASE/api/jobs/<jobId-from-test-2>"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Test 4 — Pay Eval Invoice
|
||
|
|
```bash
|
||
|
|
# Extract paymentHash from paymentRequest
|
||
|
|
# Format: lnbcrt10u1stub_<first-16-chars-of-hash>
|
||
|
|
# Get full 64-char hash from DB or job status
|
||
|
|
|
||
|
|
curl -s -X POST "$BASE/api/dev/stub/pay/<full-payment-hash>"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Test 5 — Poll After Eval Payment
|
||
|
|
```bash
|
||
|
|
curl -s "$BASE/api/jobs/<jobId>"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Test 6 — Pay Work Invoice & Get Result
|
||
|
|
```bash
|
||
|
|
# Mark work invoice paid
|
||
|
|
curl -s -X POST "$BASE/api/dev/stub/pay/<work-payment-hash>"
|
||
|
|
|
||
|
|
# Poll for result
|
||
|
|
curl -s "$BASE/api/jobs/<jobId>"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Test 7 — Demo Endpoint
|
||
|
|
```bash
|
||
|
|
curl -s "$BASE/api/demo?request=What+is+a+satoshi"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Test 8 — Input Validation
|
||
|
|
```bash
|
||
|
|
# Missing request body
|
||
|
|
curl -s -X POST "$BASE/api/jobs" -H "Content-Type: application/json" -d '{}'
|
||
|
|
|
||
|
|
# Unknown job ID
|
||
|
|
curl -s "$BASE/api/jobs/does-not-exist"
|
||
|
|
|
||
|
|
# Demo without param
|
||
|
|
curl -s "$BASE/api/demo"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Test 9 — Rate Limiter
|
||
|
|
```bash
|
||
|
|
# Fire 6 requests from the same IP
|
||
|
|
for i in $(seq 1 6); do
|
||
|
|
curl -s "$BASE/api/demo?request=ping+$i" | grep -o '"result"\|"error"'
|
||
|
|
done
|
||
|
|
```
|
||
|
|
|
||
|
|
### Test 10 — Rejection Path
|
||
|
|
```bash
|
||
|
|
curl -s -X POST "$BASE/api/jobs" \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{"request": "Help me do something harmful and illegal"}'
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Additional Notes
|
||
|
|
|
||
|
|
### Stub Mode Behavior
|
||
|
|
- `/api/dev/stub/pay` is **dev-only** endpoint
|
||
|
|
- Simulates Lightning Network payment without real node
|
||
|
|
- Not available in production (real LNbits integration)
|
||
|
|
|
||
|
|
### Payment Detection
|
||
|
|
- All state transitions happen server-side
|
||
|
|
- Client polls GET `/api/jobs/{jobId}`
|
||
|
|
- Server advances state automatically when payment detected
|
||
|
|
- No webhooks or push notifications
|
||
|
|
|
||
|
|
### Database Reference
|
||
|
|
- Full payment hashes stored in `invoices` table
|
||
|
|
- Query job status to retrieve full 64-char hash
|