Files
timmy-tower/attached_assets/timmy_api_technical_specs_1773854936781.md
alexpaynex 53bc93a9b4 Add automated testing script and expose payment hashes
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
2026-03-18 17:30:13 +00:00

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/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