Files
timmy-tower/lib/api-spec/openapi.yaml
alexpaynex 69eba6190d Task #6: Cost-based work fee pricing with BTC oracle
- btc-oracle.ts: CoinGecko BTC/USD fetch (60s cache), usdToSats() helper,
  fallback to BTC_PRICE_USD_FALLBACK env var (default $100k), 5s abort timeout
- pricing.ts: Full rewrite — per-model token rates (Haiku/Sonnet, env-var
  overridable), DO infra amortisation, originator margin %, estimateInputTokens(),
  estimateOutputTokens() by request tier, calculateActualCostUsd() for post-work ledger,
  async calculateWorkFeeSats() → WorkFeeBreakdown
- agent.ts: WorkResult now includes inputTokens + outputTokens from Anthropic usage;
  workModel/evalModel exposed as readonly public; EVAL_MODEL/WORK_MODEL env var support
- jobs.ts: Work invoice creation calls pricingService.calculateWorkFeeSats() async;
  stores estimatedCostUsd/marginPct/btcPriceUsd on job; after executeWork stores
  actualInputTokens/actualOutputTokens/actualCostUsd; GET response includes
  pricingBreakdown (awaiting_work_payment) and costLedger (complete)
- lib/db/src/schema/jobs.ts: 6 new real/integer columns for cost tracking; schema pushed
- openapi.yaml: PricingBreakdown + CostLedger schemas added to JobStatusResponse
- replit.md: 17 new env vars documented in Cost-based work fee pricing section
2026-03-18 19:20:34 +00:00

245 lines
6.4 KiB
YAML

openapi: 3.1.0
info:
# Do not change the title, if the title changes, the import paths will be broken
title: Api
version: 0.1.0
description: API specification
servers:
- url: /api
description: Base API path
tags:
- name: health
description: Health operations
- name: jobs
description: Payment-gated agent job operations
- name: demo
description: Free demo endpoint (rate-limited)
paths:
/healthz:
get:
operationId: healthCheck
tags: [health]
summary: Health check
description: Returns server health status
responses:
"200":
description: Healthy
content:
application/json:
schema:
$ref: "#/components/schemas/HealthStatus"
/jobs:
post:
operationId: createJob
tags: [jobs]
summary: Create a new agent job
description: Accepts a request, creates a job row, and issues an eval fee Lightning invoice.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateJobRequest"
responses:
"201":
description: Job created successfully
content:
application/json:
schema:
$ref: "#/components/schemas/CreateJobResponse"
"400":
description: Invalid request body
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/jobs/{id}:
get:
operationId: getJob
tags: [jobs]
summary: Get job status
description: Returns current job state. Automatically advances the state machine when a pending invoice is found to be paid.
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
"200":
description: Job status
content:
application/json:
schema:
$ref: "#/components/schemas/JobStatusResponse"
"404":
description: Job not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/demo:
get:
operationId: runDemo
tags: [demo]
summary: Free demo (rate-limited)
description: Runs the agent without payment. Limited to 5 requests per IP per hour.
parameters:
- name: request
in: query
required: true
schema:
type: string
responses:
"200":
description: Demo result
content:
application/json:
schema:
$ref: "#/components/schemas/DemoResponse"
"400":
description: Missing or invalid request param
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"429":
description: Rate limit exceeded
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
components:
schemas:
HealthStatus:
type: object
properties:
status:
type: string
required:
- status
ErrorResponse:
type: object
required:
- error
properties:
error:
type: string
InvoiceInfo:
type: object
required:
- paymentRequest
- amountSats
properties:
paymentRequest:
type: string
amountSats:
type: integer
CreateJobRequest:
type: object
required:
- request
properties:
request:
type: string
minLength: 1
CreateJobResponse:
type: object
required:
- jobId
- evalInvoice
properties:
jobId:
type: string
evalInvoice:
$ref: "#/components/schemas/InvoiceInfo"
JobState:
type: string
enum:
- awaiting_eval_payment
- evaluating
- rejected
- awaiting_work_payment
- executing
- complete
- failed
PricingBreakdown:
type: object
description: Cost breakdown shown with the work invoice (estimations at invoice-creation time)
properties:
estimatedCostUsd:
type: number
description: Total estimated cost in USD (token cost + DO infra + margin)
marginPct:
type: number
description: Originator margin percentage applied
btcPriceUsd:
type: number
description: BTC/USD spot price used to convert the invoice to sats
CostLedger:
type: object
description: Actual cost record stored after the job completes
properties:
actualInputTokens:
type: integer
actualOutputTokens:
type: integer
actualCostUsd:
type: number
description: Raw Anthropic token cost (no infra, no margin)
estimatedCostUsd:
type: number
marginPct:
type: number
btcPriceUsd:
type: number
JobStatusResponse:
type: object
required:
- jobId
- state
properties:
jobId:
type: string
state:
$ref: "#/components/schemas/JobState"
evalInvoice:
$ref: "#/components/schemas/InvoiceInfo"
workInvoice:
$ref: "#/components/schemas/InvoiceInfo"
pricingBreakdown:
$ref: "#/components/schemas/PricingBreakdown"
reason:
type: string
result:
type: string
costLedger:
$ref: "#/components/schemas/CostLedger"
errorMessage:
type: string
DemoResponse:
type: object
required:
- result
properties:
result:
type: string