Integrates a new bash script for automated end-to-end testing of the Timmy API. Updates API routes to expose payment hashes in stub mode for easier invoice payment simulation during testing. Modifies test plan documentation to include the new automated script. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 6f2776b0-a913-41d3-a988-759a82feb6f3 Replit-Helium-Checkpoint-Created: true
8.2 KiB
8.2 KiB
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:
curl -s "$BASE/api/healthz"
Response:
{"status":"ok"}
Status: HTTP 200
Test 2 — Create Job
Request:
curl -s -X POST "$BASE/api/jobs" \
-H "Content-Type: application/json" \
-d '{"request": "Explain the Lightning Network in two sentences"}'
Response:
{
"jobId": "<uuid>",
"evalInvoice": {
"paymentRequest": "lnbcrt10u1stub_...",
"amountSats": 10
}
}
Status: HTTP 201
Test 3 — Poll Job (Before Payment)
Request:
curl -s "$BASE/api/jobs/<jobId-from-test-2>"
Response:
{
"jobId": "...",
"state": "awaiting_eval_payment",
"evalInvoice": { "paymentRequest": "...", "amountSats": 10 }
}
Test 4 — Pay Eval Invoice (Stub Mode)
Request:
curl -s -X POST "$BASE/api/dev/stub/pay/<full-payment-hash>"
Response:
{"ok":true,"paymentHash":"..."}
Status: HTTP 200
Test 5 — Poll After Eval Payment (Accepted)
Request:
curl -s "$BASE/api/jobs/<jobId>"
Response (Accepted):
{
"jobId": "...",
"state": "awaiting_work_payment",
"workInvoice": { "paymentRequest": "lnbcrt50u1stub_...", "amountSats": 50 }
}
Response (Rejected):
{ "jobId": "...", "state": "rejected", "reason": "..." }
Test 6 — Pay Work Invoice & Get Result
Request (Pay Work Invoice):
curl -s -X POST "$BASE/api/dev/stub/pay/<work-payment-hash>"
Request (Poll for Result):
curl -s "$BASE/api/jobs/<jobId>"
Response:
{
"jobId": "...",
"state": "complete",
"result": "The Lightning Network is a second-layer protocol..."
}
Test 7 — Demo Endpoint
Request:
curl -s "$BASE/api/demo?request=What+is+a+satoshi"
Response:
{"result":"A satoshi is the smallest unit of Bitcoin..."}
Status: HTTP 200
Test 8 — Input Validation (Error Responses)
Missing Request Body:
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:
curl -s "$BASE/api/jobs/does-not-exist"
Response: {"error":"Job not found"} (HTTP 404)
Demo Without Param:
curl -s "$BASE/api/demo"
Response: {"error":"Missing required query param: request"} (HTTP 400)
Test 9 — Rate Limiter
Request:
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:
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:
{ "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
curl -s "$BASE/api/healthz"
Test 2 — Create Job
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
curl -s "$BASE/api/jobs/<jobId-from-test-2>"
Test 4 — Pay Eval Invoice
# 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
curl -s "$BASE/api/jobs/<jobId>"
Test 6 — Pay Work Invoice & Get Result
# 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
curl -s "$BASE/api/demo?request=What+is+a+satoshi"
Test 8 — Input Validation
# 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
# 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
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/payis 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
invoicestable - Query job status to retrieve full 64-char hash