Files
timmy-tower/lib/api-spec/openapi.yaml
Replit Agent e86dab0d65 feat: Gemini AI integration — conversations, messages, image gen
- Fixed YAML parse error (unquoted colon in description broke @scalar/json-magic)
- Converted orval.config.ts → orval.config.cjs (fixes orval v8 TypeScript config loading)
- Codegen now works: zod schemas + React Query hooks regenerated with Gemini types
- Added Gemini tag, 4 path groups, 8 schemas to openapi.yaml
- lib/integrations-gemini-ai wired: tsconfig refs, api-server package.json dep
- Created routes/gemini.ts: CRUD conversations/messages + SSE chat stream + image gen
- Mounted /gemini router in routes/index.ts
2026-03-20 02:41:12 +00:00

882 lines
25 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 (Mode 1 -- per-job)
- name: sessions
description: Pre-funded session balance mode (Mode 2 -- pay once, run many)
- name: demo
description: Free demo endpoint (rate-limited)
- name: gemini
description: Gemini AI chat and image operations
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"
/jobs/{id}/refund:
post:
operationId: claimRefund
tags: [jobs]
summary: Claim a refund for overpayment
description: |
After a job completes, if the actual cost (tokens used + infra + margin) was
less than the work invoice amount, the difference is owed back to the user.
Submit a BOLT11 invoice for exactly `refundAmountSats` to receive the payment.
Idempotent: returns 409 if already paid or if no refund is owed.
parameters:
- name: id
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ClaimRefundRequest"
responses:
"200":
description: Refund sent
content:
application/json:
schema:
$ref: "#/components/schemas/ClaimRefundResponse"
"400":
description: Missing invoice, wrong amount, or invalid BOLT11
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"404":
description: Job not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"409":
description: Job not complete, refund already paid, or no refund owed
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"500":
description: Server error (e.g. Lightning payment failure)
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/sessions:
post:
operationId: createSession
tags: [sessions]
summary: Create a pre-funded session
description: |
Opens a new session. Pay the returned Lightning invoice to activate it.
Once active, use the `macaroon` from GET /sessions/:id to authenticate requests.
Deposits: 10010,000 sats. Sessions expire after 24 h of inactivity.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateSessionRequest"
responses:
"201":
description: Session created -- awaiting deposit payment
content:
application/json:
schema:
$ref: "#/components/schemas/CreateSessionResponse"
"400":
description: Invalid amount
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/sessions/{id}:
get:
operationId: getSession
tags: [sessions]
summary: Get session status
description: Returns current state, balance, and pending invoice info. Auto-advances on payment.
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
"200":
description: Session status
content:
application/json:
schema:
$ref: "#/components/schemas/SessionStatusResponse"
"404":
description: Session not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/sessions/{id}/request:
post:
operationId: submitSessionRequest
tags: [sessions]
summary: Submit a request against a session balance
description: |
Runs eval + work and debits the actual compute cost from the session balance.
Rejected requests still incur a small eval fee. Requires `Authorization: Bearer <macaroon>`.
parameters:
- name: id
in: path
required: true
schema:
type: string
security:
- sessionMacaroon: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SessionRequestBody"
responses:
"200":
description: Request completed (or rejected)
content:
application/json:
schema:
$ref: "#/components/schemas/SessionRequestResponse"
"401":
description: Missing or invalid macaroon
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"402":
description: Insufficient balance
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"409":
description: Session not active
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"410":
description: Session expired
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/sessions/{id}/topup:
post:
operationId: topupSession
tags: [sessions]
summary: Add sats to a session
description: |
Creates a new Lightning invoice to top up the session balance.
Only one pending topup at a time. Paying it resumes a paused session.
parameters:
- name: id
in: path
required: true
schema:
type: string
security:
- sessionMacaroon: []
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateSessionRequest"
responses:
"200":
description: Topup invoice created
content:
application/json:
schema:
$ref: "#/components/schemas/TopupSessionResponse"
"400":
description: Invalid amount
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"401":
description: Invalid macaroon
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"409":
description: Session not active/paused, or topup already pending
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
headers:
X-RateLimit-Limit:
schema:
type: integer
description: Maximum requests allowed per window (always 5)
X-RateLimit-Remaining:
schema:
type: integer
description: Requests remaining in the current window
X-RateLimit-Reset:
schema:
type: integer
description: Unix epoch seconds when the rate limit window resets
content:
application/json:
schema:
$ref: "#/components/schemas/DemoResponse"
"400":
description: Missing or invalid request param
headers:
X-RateLimit-Limit:
schema:
type: integer
X-RateLimit-Remaining:
schema:
type: integer
X-RateLimit-Reset:
schema:
type: integer
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"429":
description: Rate limit exceeded
headers:
X-RateLimit-Limit:
schema:
type: integer
X-RateLimit-Remaining:
schema:
type: integer
description: Always 0 when rate limited
X-RateLimit-Reset:
schema:
type: integer
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
"500":
description: Server error
headers:
X-RateLimit-Limit:
schema:
type: integer
X-RateLimit-Remaining:
schema:
type: integer
X-RateLimit-Reset:
schema:
type: integer
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/gemini/conversations:
get:
operationId: listGeminiConversations
tags: [gemini]
summary: List all conversations
responses:
"200":
description: List of conversations
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/GeminiConversation"
post:
operationId: createGeminiConversation
tags: [gemini]
summary: Create a new conversation
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateGeminiConversationBody"
responses:
"201":
description: Created conversation
content:
application/json:
schema:
$ref: "#/components/schemas/GeminiConversation"
/gemini/conversations/{id}:
get:
operationId: getGeminiConversation
tags: [gemini]
summary: Get conversation with messages
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Conversation with messages
content:
application/json:
schema:
$ref: "#/components/schemas/GeminiConversationWithMessages"
"404":
description: Not found
content:
application/json:
schema:
$ref: "#/components/schemas/GeminiError"
delete:
operationId: deleteGeminiConversation
tags: [gemini]
summary: Delete a conversation
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"204":
description: Deleted
"404":
description: Not found
content:
application/json:
schema:
$ref: "#/components/schemas/GeminiError"
/gemini/conversations/{id}/messages:
get:
operationId: listGeminiMessages
tags: [gemini]
summary: List messages in a conversation
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: List of messages
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/GeminiMessage"
post:
operationId: sendGeminiMessage
tags: [gemini]
summary: Send a message and receive an AI response (SSE stream)
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SendGeminiMessageBody"
responses:
"200":
description: SSE stream of assistant response chunks
content:
text/event-stream: {}
/gemini/generate-image:
post:
operationId: generateGeminiImage
tags: [gemini]
summary: Generate an image from a text prompt
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/GenerateGeminiImageBody"
responses:
"200":
description: Generated image
content:
application/json:
schema:
$ref: "#/components/schemas/GenerateGeminiImageResponse"
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
- createdAt
- evalInvoice
properties:
jobId:
type: string
createdAt:
type: string
format: date-time
description: ISO 8601 timestamp of job creation
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: Honest post-work accounting stored after the job completes
properties:
actualInputTokens:
type: integer
actualOutputTokens:
type: integer
totalTokens:
type: integer
description: Sum of actualInputTokens + actualOutputTokens
actualCostUsd:
type: number
description: Raw Anthropic token cost (no infra, no margin)
actualChargeUsd:
type: number
description: What we honestly charged in USD (actual token cost + DO infra + margin)
estimatedCostUsd:
type: number
description: Original estimate used to create the work invoice
actualAmountSats:
type: integer
description: Honest sats charge (actual cost converted at the locked BTC price)
workAmountSats:
type: integer
description: Amount the user originally paid in sats
refundAmountSats:
type: integer
description: Sats owed back to the user (workAmountSats - actualAmountSats, >= 0)
refundState:
type: string
enum: [not_applicable, pending, paid]
description: Lifecycle of the refund for this job
marginPct:
type: number
btcPriceUsd:
type: number
description: BTC/USD price locked at invoice creation time
JobStatusResponse:
type: object
required:
- jobId
- state
- createdAt
properties:
jobId:
type: string
state:
$ref: "#/components/schemas/JobState"
createdAt:
type: string
format: date-time
description: ISO 8601 timestamp of job creation (always present)
completedAt:
type: string
format: date-time
nullable: true
description: ISO 8601 timestamp of job completion; null when not yet complete
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
SessionState:
type: string
enum: [awaiting_payment, active, paused, expired]
SessionInvoiceInfo:
type: object
properties:
paymentRequest:
type: string
amountSats:
type: integer
paymentHash:
type: string
description: Only present in stub/dev mode
CreateSessionRequest:
type: object
required: [amount_sats]
properties:
amount_sats:
type: integer
description: Deposit amount (10010,000 sats)
minimum: 100
maximum: 10000
CreateSessionResponse:
type: object
required: [sessionId, state, invoice]
properties:
sessionId:
type: string
state:
$ref: "#/components/schemas/SessionState"
invoice:
$ref: "#/components/schemas/SessionInvoiceInfo"
SessionStatusResponse:
type: object
required: [sessionId, state, balanceSats]
properties:
sessionId:
type: string
state:
$ref: "#/components/schemas/SessionState"
balanceSats:
type: integer
minimumBalanceSats:
type: integer
macaroon:
type: string
description: Bearer token for authenticating requests; present when active or paused
expiresAt:
type: string
format: date-time
invoice:
$ref: "#/components/schemas/SessionInvoiceInfo"
description: Present when state is awaiting_payment
pendingTopup:
$ref: "#/components/schemas/SessionInvoiceInfo"
description: Present when a topup invoice is outstanding
SessionRequestBody:
type: object
required: [request]
properties:
request:
type: string
minLength: 1
SessionCostBreakdown:
type: object
properties:
evalSats:
type: integer
workSats:
type: integer
totalSats:
type: integer
btcPriceUsd:
type: number
SessionRequestResponse:
type: object
required: [requestId, state, debitedSats, balanceRemaining]
properties:
requestId:
type: string
state:
type: string
enum: [complete, rejected, failed]
result:
type: string
reason:
type: string
errorMessage:
type: string
debitedSats:
type: integer
balanceRemaining:
type: integer
cost:
$ref: "#/components/schemas/SessionCostBreakdown"
TopupSessionResponse:
type: object
required: [sessionId, topup]
properties:
sessionId:
type: string
topup:
$ref: "#/components/schemas/SessionInvoiceInfo"
ClaimRefundRequest:
type: object
required:
- invoice
properties:
invoice:
type: string
description: BOLT11 invoice for exactly refundAmountSats
ClaimRefundResponse:
type: object
required:
- ok
- refundAmountSats
- paymentHash
- message
properties:
ok:
type: boolean
refundAmountSats:
type: integer
paymentHash:
type: string
message:
type: string
DemoResponse:
type: object
required:
- result
properties:
result:
type: string
GeminiConversation:
type: object
required: [id, title, createdAt]
properties:
id:
type: integer
title:
type: string
createdAt:
type: string
format: date-time
GeminiMessage:
type: object
required: [id, conversationId, role, content, createdAt]
properties:
id:
type: integer
conversationId:
type: integer
role:
type: string
content:
type: string
createdAt:
type: string
format: date-time
GeminiConversationWithMessages:
type: object
required: [id, title, createdAt, messages]
properties:
id:
type: integer
title:
type: string
createdAt:
type: string
format: date-time
messages:
type: array
items:
$ref: "#/components/schemas/GeminiMessage"
CreateGeminiConversationBody:
type: object
required: [title]
properties:
title:
type: string
SendGeminiMessageBody:
type: object
required: [content]
properties:
content:
type: string
model:
type: string
description: "Gemini model override (default: gemini-3-flash-preview)"
GenerateGeminiImageBody:
type: object
required: [prompt]
properties:
prompt:
type: string
GenerateGeminiImageResponse:
type: object
required: [b64_json, mimeType]
properties:
b64_json:
type: string
mimeType:
type: string
GeminiError:
type: object
required: [error]
properties:
error:
type: string
securitySchemes:
sessionMacaroon:
type: http
scheme: bearer
description: "Session macaroon issued when a session activates. Pass as `Authorization: Bearer <macaroon>`."