@anuma/portal 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -0
- package/package.json +29 -0
- package/swagger.json +7 -0
package/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# AI Portal
|
|
2
|
+
|
|
3
|
+
AI Portal is a prepaid escrow-based API gateway for wallet-authenticated LLM
|
|
4
|
+
usage. It provides an OpenAI-compatible API with on-chain settlement for AI
|
|
5
|
+
inference costs.
|
|
6
|
+
|
|
7
|
+
- `server`: Handles API requests, user authentication, billing, and manages
|
|
8
|
+
communication with LLM providers.
|
|
9
|
+
- `worker`: Processes AI inference settlements.
|
|
10
|
+
|
|
11
|
+
## Getting Started
|
|
12
|
+
|
|
13
|
+
### Prerequisites
|
|
14
|
+
|
|
15
|
+
- Go 1.23+
|
|
16
|
+
- PostgreSQL 15+
|
|
17
|
+
- ZetaChain RPC endpoint
|
|
18
|
+
- golangci-lint v2 (for development)
|
|
19
|
+
```bash
|
|
20
|
+
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Accessing the Private Escrow Contract Repository
|
|
24
|
+
|
|
25
|
+
The escrow contract sources live in the private `github.com/zeta-chain/escrow-contract` repository. Configure Go and Git to always use SSH when cloning from `github.com/zeta-chain/*` to avoid authentication errors:
|
|
26
|
+
|
|
27
|
+
1. Set `GOPRIVATE` (note that you can add this to your shell profile):
|
|
28
|
+
```bash
|
|
29
|
+
export GOPRIVATE=github.com/zeta-chain/*
|
|
30
|
+
```
|
|
31
|
+
2. Update your `~/.gitconfig` to prefer the SSH remote for GitHub:
|
|
32
|
+
```ini
|
|
33
|
+
[url "git@github.com:"]
|
|
34
|
+
insteadOf = https://github.com/
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
make install
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Install only server**
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
make install-server
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Install only worker**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
make install-worker
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Configuration
|
|
56
|
+
|
|
57
|
+
See `.env.example` for available configuration options. Create `.env` with based on this file.
|
|
58
|
+
|
|
59
|
+
### Running Tests
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
make test
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Generate API specification and client
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
make generate
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This command regenerates both the Swagger specification in `docs` and the
|
|
72
|
+
TypeScript client in `typescript`.
|
|
73
|
+
|
|
74
|
+
### Run E2E Test
|
|
75
|
+
|
|
76
|
+
The e2e tests use two configuration files:
|
|
77
|
+
- **`.env.test`** (committed): All public test configuration values
|
|
78
|
+
- **`.env.test.local`** (gitignored): Secrets like `GITHUB_PAT` and optional Privy credentials
|
|
79
|
+
|
|
80
|
+
**Setup:**
|
|
81
|
+
1. The `.env.test` file is already in the repository with all public test values
|
|
82
|
+
2. Create a `.env.test.local` file (gitignored) with your secrets:
|
|
83
|
+
```bash
|
|
84
|
+
# Required for Docker builds (private repos)
|
|
85
|
+
GITHUB_PAT=your_github_pat_here
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Authentication coverage:**
|
|
89
|
+
|
|
90
|
+
| E2E test | Authentication |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| `chat_completion` | Privy JWT (Bearer token) |
|
|
93
|
+
| `chat_completion_stream` | Privy JWT (Bearer token) |
|
|
94
|
+
| `embeddings` | Privy JWT (Bearer token) |
|
|
95
|
+
| `response_api_chat` | Privy JWT (Bearer token) |
|
|
96
|
+
| `response_api_image_gen` | Privy JWT (Bearer token) |
|
|
97
|
+
| `chat_completions_image` | Privy JWT (Bearer token) |
|
|
98
|
+
| `chat_completion_api_key` | API key (`X-API-Key`) |
|
|
99
|
+
| `chat_completion_stream_api_key` | API key (`X-API-Key`) |
|
|
100
|
+
|
|
101
|
+
**Run E2E tests:**
|
|
102
|
+
|
|
103
|
+
Single command to start everything and run E2E tests:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
make test-e2e
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This will:
|
|
110
|
+
1. Start all Docker services (Anvil, Postgres, Server, Worker)
|
|
111
|
+
2. Deploy contracts to Anvil
|
|
112
|
+
3. Submit api requests
|
|
113
|
+
4. Worker detects contracts and starts processing
|
|
114
|
+
5. Verify entries in Postgres DB are created
|
|
115
|
+
|
|
116
|
+
### Running with Frontend
|
|
117
|
+
|
|
118
|
+
To run the local development environment with the frontend UI:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
make localnet-frontend
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Note**: The frontend image (`ai-memoryless-client`) must be built first from the [ai-memoryless-client](https://github.com/zeta-chain/ai-memoryless-client) repository:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
docker build -f apps/web/Dockerfile -t ai-memoryless-client \
|
|
128
|
+
--build-arg NEXT_PUBLIC_API_URL=http://localhost:8080 \
|
|
129
|
+
--build-arg NEXT_PUBLIC_PRIVY_APP_ID=<privy-app-id> \
|
|
130
|
+
.
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The frontend will be available at http://localhost:8787.
|
|
134
|
+
|
|
135
|
+
## Deployment
|
|
136
|
+
|
|
137
|
+
AI Portal is deployed to two environments:
|
|
138
|
+
|
|
139
|
+
| Environment | URL | Deployment |
|
|
140
|
+
|-------------|-----|------------|
|
|
141
|
+
| **Dev** | https://portal.anuma-dev.ai | Automatic on push to `main` |
|
|
142
|
+
| **Prod** | https://portal.anuma.ai | Manual with version tags |
|
|
143
|
+
|
|
144
|
+
See [INFRASTRUCTURE.md](INFRASTRUCTURE.md) for detailed deployment instructions and CI/CD documentation.
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anuma/portal",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Anuma Portal is a prepaid escrow-based API gateway for wallet-authenticated LLM usage. It provides an OpenAI-compatible API with on-chain settlement for AI inference costs.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"directories": {
|
|
7
|
+
"doc": "docs"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
+
"prepack": "node -e \"require('fs').copyFileSync('docs/swagger.json','swagger.json')\"",
|
|
12
|
+
"postpack": "node -e \"try{require('fs').unlinkSync('swagger.json')}catch(e){if(e.code!=='ENOENT')throw e;}\""
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/zeta-chain/ai-portal.git"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [],
|
|
19
|
+
"author": "",
|
|
20
|
+
"license": "ISC",
|
|
21
|
+
"type": "commonjs",
|
|
22
|
+
"files": [
|
|
23
|
+
"swagger.json"
|
|
24
|
+
],
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/zeta-chain/ai-portal/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/zeta-chain/ai-portal#readme"
|
|
29
|
+
}
|
package/swagger.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"components": {"schemas":{"handlers.APIKeyResponse":{"properties":{"app_id":{"type":"integer"},"created_at":{"type":"string"},"has_key":{"description":"Indicates if key is set, but doesn't expose it","type":"boolean"},"id":{"type":"integer"},"is_active":{"type":"boolean"},"name":{"type":"string"},"updated_at":{"type":"string"},"wallet_address":{"type":"string"},"wallet_details":{"$ref":"#/components/schemas/handlers.WalletDetails"}},"type":"object"},"handlers.APIKeyWithKeyResponse":{"properties":{"api_key":{"description":"Only included in create response","type":"string"},"app_id":{"type":"integer"},"created_at":{"type":"string"},"id":{"type":"integer"},"is_active":{"type":"boolean"},"name":{"type":"string"},"updated_at":{"type":"string"},"wallet_address":{"type":"string"}},"type":"object"},"handlers.AddCreditsRequest":{"properties":{"credits":{"description":"Number of credits to add (1 credit = 1 cent)","type":"integer"},"escrow_contract":{"description":"Escrow contract address to use","type":"string"},"user_address":{"type":"string"}},"type":"object"},"handlers.AddCreditsResponse":{"properties":{"credits_added":{"type":"integer"},"escrow_contract":{"description":"Escrow contract used for the operation","type":"string"},"message":{"type":"string"},"success":{"type":"boolean"},"user_address":{"type":"string"}},"type":"object"},"handlers.AppConfig":{"properties":{"escrow_contract":{"description":"EscrowContract is the escrow contract address for this app","type":"string"},"name":{"description":"Name is the human-readable name of the app","type":"string"}},"type":"object"},"handlers.AppResponse":{"properties":{"created_at":{"type":"string"},"credit_reset_enabled":{"type":"boolean"},"escrow_contract":{"type":"string"},"has_privy_verification_key":{"description":"Indicates if key is set, but doesn't expose it","type":"boolean"},"id":{"type":"integer"},"is_active":{"type":"boolean"},"name":{"type":"string"},"privy_app_id":{"type":"string"},"updated_at":{"type":"string"}},"type":"object"},"handlers.CancelSubscriptionResponse":{"properties":{"cancel_at":{"type":"integer"},"current_period_end":{"type":"integer"},"message":{"type":"string"}},"type":"object"},"handlers.CheckoutSessionResponse":{"properties":{"url":{"type":"string"}},"type":"object"},"handlers.ClaimDailyCreditsResponse":{"properties":{"credits_awarded":{"description":"Credits awarded (1 credit = $0.01)","type":"integer"},"message":{"type":"string"},"next_claim_at":{"description":"ISO8601 timestamp when next claim is available","type":"string"},"success":{"type":"boolean"}},"type":"object"},"handlers.ConfigResponse":{"properties":{"apps":{"description":"Apps is the list of active apps with their escrow contracts","items":{"$ref":"#/components/schemas/handlers.AppConfig"},"type":"array","uniqueItems":false},"chain_id":{"description":"ChainID is the blockchain chain ID","type":"string"},"operator_address":{"description":"OperatorAddress is the operator wallet address","type":"string"},"settlement_recipient":{"description":"SettlementRecipient is the address that receives settlement payments","type":"string"}},"type":"object"},"handlers.CreateAPIKeyRequest":{"properties":{"is_active":{"type":"boolean"},"is_test":{"description":"If true, generates anuma_test_ prefix; otherwise anuma_live_","type":"boolean"},"name":{"type":"string"},"wallet_address":{"type":"string"}},"type":"object"},"handlers.CreateAppRequest":{"properties":{"credit_reset_enabled":{"type":"boolean"},"escrow_contract":{"type":"string"},"is_active":{"type":"boolean"},"name":{"type":"string"},"privy_app_id":{"type":"string"},"privy_verification_key":{"type":"string"}},"type":"object"},"handlers.CreateCheckoutSessionRequest":{"properties":{"cancel_url":{"type":"string"},"success_url":{"type":"string"}},"type":"object"},"handlers.CreateCreditPackCheckoutRequest":{"properties":{"cancel_url":{"type":"string"},"credits":{"type":"integer"},"success_url":{"type":"string"}},"type":"object"},"handlers.CreateCustomerPortalRequest":{"properties":{"return_url":{"type":"string"}},"type":"object"},"handlers.CreditBalanceResponse":{"properties":{"available_credits":{"description":"Available credits (1 credit = $0.01)","type":"integer"},"can_claim_daily":{"type":"boolean"},"is_enrolled":{"description":"Whether enrolled on-chain","type":"boolean"},"last_claim_at":{"type":"string"},"lifetime_credits":{"description":"Total credits ever received (1 credit = $0.01)","type":"integer"},"next_claim_at":{"type":"string"},"subscription_tier":{"description":"\"basic\" or \"pro\"","type":"string"},"wallet_address":{"type":"string"}},"type":"object"},"handlers.CreditPack":{"properties":{"bonus_percent":{"type":"integer"},"credits":{"type":"integer"},"currency":{"type":"string"},"pro_credits":{"type":"integer"},"unit_amount":{"type":"integer"}},"type":"object"},"handlers.CreditPacksResponse":{"properties":{"packs":{"items":{"$ref":"#/components/schemas/handlers.CreditPack"},"type":"array","uniqueItems":false}},"type":"object"},"handlers.CustomerPortalResponse":{"properties":{"url":{"type":"string"}},"type":"object"},"handlers.ExchangeRequest":{"properties":{"code":{"type":"string"},"redirect_uri":{"description":"Optional - uses config default if not provided","type":"string"}},"required":["code"],"type":"object"},"handlers.GeneratedAPIKey":{"properties":{"api_key":{"description":"Full HMAC key (anuma_live_\u003ckey_id\u003e.\u003csecret\u003e or anuma_test_\u003ckey_id\u003e.\u003csecret\u003e)","type":"string"},"name":{"type":"string"}},"type":"object"},"handlers.GetToolsResponse":{"properties":{"checksum":{"type":"string"},"tools":{"additionalProperties":{"$ref":"#/components/schemas/handlers.Tool"},"type":"object"}},"type":"object"},"handlers.HealthResponse":{"properties":{"status":{"description":"Status indicates the service health status","type":"string"},"timestamp":{"description":"Timestamp is the Unix timestamp of the response","type":"integer"},"version":{"description":"Version is the current API version","type":"string"}},"type":"object"},"handlers.ListAPIKeysResponse":{"properties":{"api_keys":{"items":{"$ref":"#/components/schemas/handlers.APIKeyResponse"},"type":"array","uniqueItems":false}},"type":"object"},"handlers.ListAppsResponse":{"properties":{"apps":{"items":{"$ref":"#/components/schemas/handlers.AppResponse"},"type":"array","uniqueItems":false}},"type":"object"},"handlers.RefreshRequest":{"properties":{"refresh_token":{"type":"string"}},"required":["refresh_token"],"type":"object"},"handlers.RenewSubscriptionResponse":{"properties":{"current_period_end":{"type":"integer"},"message":{"type":"string"}},"type":"object"},"handlers.RevokeRequest":{"properties":{"token":{"type":"string"}},"required":["token"],"type":"object"},"handlers.SeedAPIKeyInput":{"properties":{"is_active":{"type":"boolean"},"is_test":{"description":"If true, generates anuma_test_ prefix; otherwise anuma_live_","type":"boolean"},"name":{"type":"string"},"wallet_address":{"type":"string"}},"type":"object"},"handlers.SeedAppInput":{"properties":{"api_keys":{"items":{"$ref":"#/components/schemas/handlers.SeedAPIKeyInput"},"type":"array","uniqueItems":false},"credit_reset_enabled":{"type":"boolean"},"escrow_contract":{"type":"string"},"is_active":{"type":"boolean"},"name":{"type":"string"},"privy_app_id":{"type":"string"},"privy_verification_key":{"type":"string"}},"type":"object"},"handlers.SeedAppsRequest":{"properties":{"apps":{"items":{"$ref":"#/components/schemas/handlers.SeedAppInput"},"type":"array","uniqueItems":false}},"type":"object"},"handlers.SeedAppsResponse":{"properties":{"apps_seeded":{"type":"integer"},"generated_keys":{"additionalProperties":{"items":{"$ref":"#/components/schemas/handlers.GeneratedAPIKey"},"type":"array"},"description":"Map of app name to generated keys","type":"object"},"keys_seeded":{"type":"integer"},"message":{"type":"string"},"success":{"type":"boolean"}},"type":"object"},"handlers.SetSubscriptionTierRequest":{"properties":{"tier":{"description":"\"basic\" or \"pro\"","type":"string"},"user_address":{"type":"string"}},"type":"object"},"handlers.SetSubscriptionTierResponse":{"properties":{"message":{"type":"string"},"success":{"type":"boolean"},"tier":{"type":"string"},"user_address":{"type":"string"}},"type":"object"},"handlers.SubscriptionStatusResponse":{"properties":{"cancel_at_period_end":{"description":"true if scheduled to cancel","type":"boolean"},"current_period_end":{"description":"Unix timestamp, only present if subscribed","type":"integer"},"plan":{"description":"\"free\" | \"pro\"","type":"string"},"status":{"description":"\"none\" | \"active\" | \"canceling\" | \"past_due\" | \"canceled\"","type":"string"}},"type":"object"},"handlers.SyncSnagResponse":{"properties":{"credits_awarded":{"type":"integer"},"message":{"type":"string"},"next_sync_at":{"type":"string"},"success":{"type":"boolean"},"total_converted":{"type":"integer"}},"type":"object"},"handlers.TaskResponse":{"properties":{"amount_credits":{"description":"1 credit = $0.01","type":"integer"},"cooldown_secs":{"description":"null for one-time tasks","type":"integer"},"description":{"type":"string"},"type":{"type":"string"}},"type":"object"},"handlers.TokenResponse":{"properties":{"access_token":{"type":"string"},"expires_in":{"description":"Seconds until expiration","type":"integer"},"refresh_token":{"description":"May not be present on refresh","type":"string"},"scope":{"description":"Granted scopes","type":"string"},"token_type":{"description":"Usually \"Bearer\"","type":"string"}},"type":"object"},"handlers.Tool":{"properties":{"cost":{"type":"integer"},"embedding":{"items":{"type":"number"},"type":"array","uniqueItems":false},"name":{"type":"string"},"schema":{"$ref":"#/components/schemas/mcp.ToolSchema"}},"type":"object"},"handlers.UpdateAPIKeyRequest":{"properties":{"app_id":{"type":"integer"},"is_active":{"type":"boolean"},"name":{"type":"string"},"wallet_address":{"type":"string"}},"type":"object"},"handlers.UpdateAppRequest":{"properties":{"credit_reset_enabled":{"type":"boolean"},"escrow_contract":{"type":"string"},"is_active":{"type":"boolean"},"name":{"type":"string"},"privy_app_id":{"type":"string"},"privy_verification_key":{"type":"string"}},"type":"object"},"handlers.WalletDetails":{"description":"Wallet account details","properties":{"account_created_at":{"description":"When account was first created","type":"string"},"account_id":{"type":"integer"},"balance_updated_at":{"description":"When balance was last synced from chain","type":"string"},"cached_balance_usd":{"description":"Balance in micro-dollars (USD * 1,000,000)","type":"integer"},"enrolled_app_id":{"description":"App ID for enrollment (0 if not enrolled)","type":"integer"},"is_enrolled":{"description":"Whether enrolled in cost-limit model","type":"boolean"},"pending_cost_usd":{"description":"In-flight request holds in micro-dollars","type":"integer"},"pro_activated_at":{"description":"When user became Pro subscriber","type":"string"},"subscription_tier":{"description":"\"basic\" or \"pro\"","type":"string"}},"type":"object"},"llmapi.ChatCompletionExtraFields":{"description":"ExtraFields contains additional metadata","properties":{"latency":{"description":"Latency is the request latency in milliseconds","type":"integer"},"model_requested":{"description":"ModelRequested is the model that was requested","type":"string"},"provider":{"description":"Provider is the LLM provider used (e.g., \"openai\", \"anthropic\")","type":"string"},"request_type":{"description":"RequestType is always \"chat_completion\"","type":"string"}},"type":"object"},"llmapi.ChatCompletionRequest":{"properties":{"messages":{"description":"Messages is the conversation history","items":{"$ref":"#/components/schemas/llmapi.Message"},"type":"array","uniqueItems":false},"model":{"description":"Model is the model identifier","type":"string"},"stream":{"description":"Stream indicates if response should be streamed","type":"boolean"},"tool_choice":{"$ref":"#/components/schemas/llmapi.ChatCompletionToolChoice"},"tools":{"description":"Tools is an array of tool schemas describing which tools the model can use","items":{"$ref":"#/components/schemas/llmapi.ChatCompletionTool"},"type":"array","uniqueItems":false}},"required":["messages","model"],"type":"object"},"llmapi.ChatCompletionResponse":{"properties":{"choices":{"description":"Choices contains the completion choices","items":{"$ref":"#/components/schemas/llmapi.Choice"},"type":"array","uniqueItems":false},"extra_fields":{"$ref":"#/components/schemas/llmapi.ChatCompletionExtraFields"},"id":{"description":"ID is the completion ID","type":"string"},"messages":{"description":"Messages contains the full conversation history when local tools need execution.\nThis is populated when the model requests tools that are not MCP tools (local/client-side tools).\nThe client should execute these tools and send a new request with this message history\nplus the tool results appended.","items":{"$ref":"#/components/schemas/llmapi.Message"},"type":"array","uniqueItems":false},"model":{"description":"Model is the model used","type":"string"},"tool_call_events":{"description":"ToolCallEvents is an array of tool call events.","items":{"$ref":"#/components/schemas/llmapi.ToolCallEvent"},"type":"array","uniqueItems":false},"tools_checksum":{"description":"ToolsChecksum is the checksum of the tool schemas used by the AI Portal.","type":"string"},"usage":{"$ref":"#/components/schemas/llmapi.ChatCompletionUsage"}},"type":"object"},"llmapi.ChatCompletionTool":{"type":"object"},"llmapi.ChatCompletionToolChoice":{"description":"ToolChoice controls tool usage","type":"object"},"llmapi.ChatCompletionUsage":{"description":"Usage contains token usage information","properties":{"completion_tokens":{"description":"CompletionTokens is the number of tokens in the completion","type":"integer"},"cost_micro_usd":{"description":"CostMicroUSD is the cost of this completion in micro-dollars (USD × 1,000,000)","type":"integer"},"credits_used":{"description":"CreditsUsed is the number of credits consumed by this completion (ceiling of cost / MicroUSDPerCredit)","type":"integer"},"prompt_tokens":{"description":"PromptTokens is the number of tokens in the prompt","type":"integer"},"total_tokens":{"description":"TotalTokens is the total number of tokens used","type":"integer"}},"type":"object"},"llmapi.Choice":{"properties":{"finish_reason":{"description":"FinishReason indicates why the completion stopped","type":"string"},"index":{"description":"Index is the choice index","type":"integer"},"message":{"$ref":"#/components/schemas/llmapi.Message"}},"type":"object"},"llmapi.EmbeddingData":{"properties":{"embedding":{"description":"Embedding vector","items":{"type":"number"},"type":"array","uniqueItems":false},"index":{"description":"Index of the embedding","type":"integer"},"object":{"description":"Object type identifier","type":"string"}},"type":"object"},"llmapi.EmbeddingExtraFields":{"description":"ExtraFields contains additional metadata","properties":{"chunk_index":{"description":"ChunkIndex is the chunk index (0 for single requests)","type":"integer"},"latency":{"description":"Latency is the request latency in milliseconds","type":"integer"},"model_requested":{"description":"ModelRequested is the model that was requested","type":"string"},"provider":{"description":"Provider is the LLM provider used (e.g., \"openai\", \"anthropic\")","type":"string"},"request_type":{"description":"RequestType is always \"embedding\"","type":"string"}},"type":"object"},"llmapi.EmbeddingRequest":{"properties":{"dimensions":{"description":"Dimensions is the number of dimensions the resulting output embeddings should have (optional)","type":"integer"},"encoding_format":{"description":"EncodingFormat is the format to return the embeddings in (optional: \"float\" or \"base64\")","type":"string"},"input":{"description":"Input text or tokens to embed (can be string, []string, []int, or [][]int)"},"model":{"description":"Model identifier in 'provider/model' format","type":"string"}},"required":["input","model"],"type":"object"},"llmapi.EmbeddingResponse":{"properties":{"data":{"description":"Data contains the embeddings","items":{"$ref":"#/components/schemas/llmapi.EmbeddingData"},"type":"array","uniqueItems":false},"extra_fields":{"$ref":"#/components/schemas/llmapi.EmbeddingExtraFields"},"model":{"description":"Model is the model used","type":"string"},"object":{"description":"Object is always \"list\"","type":"string"},"usage":{"$ref":"#/components/schemas/llmapi.EmbeddingUsage"}},"type":"object"},"llmapi.EmbeddingUsage":{"description":"Usage contains token usage information","properties":{"cost_micro_usd":{"description":"CostMicroUSD is the inference cost for this embedding request","type":"integer"},"credits_used":{"description":"CreditsUsed is the number of credits consumed by this embedding request","type":"integer"},"prompt_tokens":{"description":"PromptTokens is the number of tokens in the prompt","type":"integer"},"total_tokens":{"description":"TotalTokens is the total number of tokens used","type":"integer"}},"type":"object"},"llmapi.MCPTool":{"properties":{"description":{"description":"Description is the description of the tool","type":"string"},"input_schema":{"description":"InputSchema is the JSON schema describing the tool's input"},"name":{"description":"Name is the name of the tool","type":"string"}},"type":"object"},"llmapi.Message":{"description":"Message is the generated message","properties":{"content":{"description":"Content is the message content","items":{"$ref":"#/components/schemas/llmapi.MessageContentPart"},"type":"array","uniqueItems":false},"role":{"$ref":"#/components/schemas/llmapi.Role"},"tool_call_id":{"description":"ToolCallID is the ID of the tool call this message is responding to (only for tool role)","type":"string"},"tool_calls":{"description":"ToolCalls contains tool/function calls made by the assistant (only for assistant role)","items":{"$ref":"#/components/schemas/llmapi.ToolCall"},"type":"array","uniqueItems":false},"type":{"description":"Type is the message type (for Responses API: \"message\")","type":"string"}},"type":"object"},"llmapi.MessageContentFile":{"description":"File is used when Type=input_file (for Responses API)","properties":{"file_data":{"description":"FileData is the base64-encoded file content","type":"string"},"file_id":{"description":"FileID is the ID of an uploaded file","type":"string"},"file_url":{"description":"FileURL is the URL to the file","type":"string"},"filename":{"description":"Filename is the name of the file","type":"string"}},"type":"object"},"llmapi.MessageContentImage":{"description":"ImageURL is used when Type=image_url or Type=input_image","properties":{"detail":{"description":"Detail is the OpenAI detail hint (auto|low|high)","type":"string"},"url":{"description":"URL is the image URL or data URI","type":"string"}},"type":"object"},"llmapi.MessageContentPart":{"properties":{"file":{"$ref":"#/components/schemas/llmapi.MessageContentFile"},"image_url":{"$ref":"#/components/schemas/llmapi.MessageContentImage"},"text":{"description":"Text holds the text content when Type=text or Type=input_text","type":"string"},"type":{"description":"Type is the block type (`text`, `image_url`, or `input_file`)","type":"string"}},"type":"object"},"llmapi.Model":{"properties":{"architecture":{"$ref":"#/components/schemas/llmapi.ModelArchitecture"},"canonical_slug":{"description":"CanonicalSlug is the canonical slug for the model","type":"string"},"context_length":{"description":"ContextLength is the maximum context length in tokens","type":"integer"},"created":{"description":"Created is the Unix timestamp of when the model was created","type":"integer"},"default_parameters":{"additionalProperties":{},"description":"DefaultParameters contains default parameter values","type":"object"},"description":{"description":"Description describes the model and its capabilities","type":"string"},"hugging_face_id":{"description":"HuggingFaceID is the Hugging Face model identifier","type":"string"},"id":{"description":"ID is the model identifier (e.g., \"openai/gpt-4\")","type":"string"},"max_input_tokens":{"description":"MaxInputTokens is the maximum input tokens","type":"integer"},"max_output_tokens":{"description":"MaxOutputTokens is the maximum output tokens","type":"integer"},"modalities":{"description":"Modalities is a list of supported modalities (e.g., [\"llm\", \"vision\"])","items":{"type":"string"},"type":"array","uniqueItems":false},"name":{"description":"Name is the human-readable model name (optional)","type":"string"},"owned_by":{"description":"OwnedBy is the organization that owns the model","type":"string"},"per_request_limits":{"$ref":"#/components/schemas/llmapi.ModelPerRequestLimits"},"pricing":{"$ref":"#/components/schemas/llmapi.ModelPricing"},"supported_methods":{"description":"SupportedMethods is a list of supported API methods","items":{"type":"string"},"type":"array","uniqueItems":false},"supported_parameters":{"description":"SupportedParameters is a list of supported parameter names","items":{"type":"string"},"type":"array","uniqueItems":false},"top_provider":{"$ref":"#/components/schemas/llmapi.ModelTopProvider"}},"type":"object"},"llmapi.ModelArchitecture":{"description":"Architecture describes the model's technical capabilities","properties":{"instruct_type":{"type":"string"},"modality":{"type":"string"},"prompt_formatting":{"type":"string"},"tokenizer":{"type":"string"}},"type":"object"},"llmapi.ModelPerRequestLimits":{"description":"PerRequestLimits contains rate limiting information","properties":{"completion_tokens":{"type":"integer"},"prompt_tokens":{"type":"integer"}},"type":"object"},"llmapi.ModelPricing":{"description":"Pricing contains the pricing structure for using this model","properties":{"completion":{"type":"string"},"image":{"type":"string"},"prompt":{"type":"string"},"request":{"type":"string"}},"type":"object"},"llmapi.ModelTopProvider":{"description":"TopProvider contains configuration details for the primary provider","properties":{"context_length":{"type":"integer"},"is_moderated":{"type":"boolean"},"max_completion_tokens":{"type":"integer"}},"type":"object"},"llmapi.ModelsListExtraFields":{"description":"ExtraFields contains additional metadata","properties":{"chunk_index":{"description":"ChunkIndex is the chunk index (0 for single requests)","type":"integer"},"latency":{"description":"Latency is the request latency in milliseconds","type":"integer"},"request_type":{"description":"RequestType is always \"list_models\"","type":"string"}},"type":"object"},"llmapi.ModelsListResponse":{"properties":{"data":{"description":"Data contains the list of available models","items":{"$ref":"#/components/schemas/llmapi.Model"},"type":"array","uniqueItems":false},"extra_fields":{"$ref":"#/components/schemas/llmapi.ModelsListExtraFields"},"next_page_token":{"description":"NextPageToken is the token to retrieve the next page of results (omitted if no more pages)","type":"string"}},"type":"object"},"llmapi.ResponseExtraFields":{"description":"ExtraFields contains additional metadata","properties":{"latency":{"description":"Latency is the request latency in milliseconds","type":"integer"},"model_requested":{"description":"ModelRequested is the model that was requested","type":"string"},"provider":{"description":"Provider is the LLM provider used (e.g., \"openai\", \"anthropic\")","type":"string"},"request_type":{"description":"RequestType is always \"responses\"","type":"string"}},"type":"object"},"llmapi.ResponseInput":{"description":"Input can be a simple text string or an array of messages for multi-turn conversations.\nWhen continuing after client tool calls, pass the messages array from the previous response.","properties":{"messages":{"description":"Messages is set when input is an array of messages (for multi-turn/tool continuations)","items":{"$ref":"#/components/schemas/llmapi.Message"},"type":"array"},"text":{"description":"Text is set when input is a simple string","type":"string"}},"type":"object"},"llmapi.ResponseOutputContent":{"properties":{"text":{"description":"Text is the text content","type":"string"},"type":{"description":"Type is the content type (e.g., \"output_text\")","type":"string"}},"type":"object"},"llmapi.ResponseOutputItem":{"properties":{"arguments":{"description":"Arguments is the function arguments for function_call and mcp_call types","type":"string"},"call_id":{"description":"CallID is the call ID for function_call and mcp_call types","type":"string"},"content":{"description":"Content is the content array for message and reasoning types","items":{"$ref":"#/components/schemas/llmapi.ResponseOutputContent"},"type":"array","uniqueItems":false},"error":{"description":"Error is the MCP error message for mcp_call types","type":"string"},"id":{"description":"ID is the unique identifier for this output item","type":"string"},"name":{"description":"Name is the function name for function_call and mcp_call types","type":"string"},"output":{"description":"Output is the MCP tool output for mcp_call types","type":"string"},"role":{"description":"Role is the role for message types (e.g., \"assistant\")","type":"string"},"server_label":{"description":"ServerLabel is the MCP server label for mcp_call and mcp_list_tools types","type":"string"},"status":{"description":"Status is the status of this output item (e.g., \"completed\")","type":"string"},"summary":{"description":"Summary is the reasoning summary for reasoning types","items":{"$ref":"#/components/schemas/llmapi.ResponseOutputContent"},"type":"array","uniqueItems":false},"tools":{"description":"Tools is the list of available tools for mcp_list_tools types","items":{"$ref":"#/components/schemas/llmapi.MCPTool"},"type":"array","uniqueItems":false},"type":{"description":"Type is the output item type (e.g., \"message\", \"function_call\", \"reasoning\", \"mcp_call\")","type":"string"}},"type":"object"},"llmapi.ResponseReasoning":{"description":"Reasoning configures reasoning for o-series and other reasoning models","properties":{"effort":{"description":"Effort controls reasoning effort: \"low\", \"medium\", or \"high\"","type":"string"},"summary":{"description":"Summary controls reasoning summary: \"auto\", \"concise\", or \"detailed\"","type":"string"}},"type":"object"},"llmapi.ResponseRequest":{"properties":{"background":{"description":"Background indicates if request should be processed in background","type":"boolean"},"input":{"$ref":"#/components/schemas/llmapi.ResponseInput"},"max_output_tokens":{"description":"MaxOutputTokens is the maximum number of tokens to generate","type":"integer"},"model":{"description":"Model is the model identifier in 'provider/model' format","type":"string"},"reasoning":{"$ref":"#/components/schemas/llmapi.ResponseReasoning"},"stream":{"description":"Stream indicates if response should be streamed","type":"boolean"},"temperature":{"description":"Temperature controls randomness (0.0 to 2.0)","type":"number"},"thinking":{"$ref":"#/components/schemas/llmapi.ThinkingOptions"},"tool_choice":{"$ref":"#/components/schemas/llmapi.ResponseToolChoice"},"tools":{"description":"Tools is an array of tool schemas describing which tools the model can use","items":{"$ref":"#/components/schemas/llmapi.ResponseTool"},"type":"array","uniqueItems":false}},"required":["input","model"],"type":"object"},"llmapi.ResponseResponse":{"properties":{"created_at":{"description":"Created is the Unix timestamp of creation (created_at in OpenAI format)","type":"integer"},"extra_fields":{"$ref":"#/components/schemas/llmapi.ResponseExtraFields"},"id":{"description":"ID is the unique response identifier","type":"string"},"messages":{"description":"Messages contains the full conversation history when local tools need execution.\nThis is populated when the model requests tools that are not MCP tools (local/client-side tools).\nThe client should execute these tools and send a new request with this message history\nplus the tool results appended.","items":{"$ref":"#/components/schemas/llmapi.Message"},"type":"array","uniqueItems":false},"model":{"description":"Model is the model used for generation","type":"string"},"object":{"description":"Object is the response type (e.g., \"response\")","type":"string"},"output":{"description":"Output is the array of output items (OpenAI Responses API format)","items":{"$ref":"#/components/schemas/llmapi.ResponseOutputItem"},"type":"array","uniqueItems":false},"tool_call_events":{"description":"ToolCallEvents is an array of tool call events.","items":{"$ref":"#/components/schemas/llmapi.ToolCallEvent"},"type":"array","uniqueItems":false},"tools_checksum":{"description":"ToolsChecksum is the checksum of the tool schemas used by the AI Portal.","type":"string"},"usage":{"$ref":"#/components/schemas/llmapi.ResponseUsage"}},"type":"object"},"llmapi.ResponseTool":{"type":"object"},"llmapi.ResponseToolChoice":{"description":"ToolChoice controls tool usage","type":"object"},"llmapi.ResponseUsage":{"description":"Usage contains token usage information","properties":{"completion_tokens":{"description":"CompletionTokens is the number of tokens in the completion","type":"integer"},"cost_micro_usd":{"description":"CostMicroUSD is the cost of this response in micro-dollars (USD × 1,000,000)","type":"integer"},"credits_used":{"description":"CreditsUsed is the number of credits consumed by this response","type":"integer"},"prompt_tokens":{"description":"PromptTokens is the number of tokens in the prompt","type":"integer"},"total_tokens":{"description":"TotalTokens is the total number of tokens used","type":"integer"}},"type":"object"},"llmapi.Role":{"description":"Role is the message role (system, user, assistant, tool)","type":"string","x-enum-varnames":["RoleAssistant","RoleUser","RoleSystem","RoleTool"]},"llmapi.ThinkingOptions":{"description":"Thinking configures extended thinking for Anthropic models","properties":{"budget_tokens":{"description":"BudgetTokens is the token budget for thinking","type":"integer"},"type":{"description":"Type indicates if thinking is enabled: \"enabled\" or \"disabled\"","type":"string"}},"type":"object"},"llmapi.ToolCall":{"properties":{"function":{"$ref":"#/components/schemas/llmapi.ToolCallFunction"},"id":{"description":"ID is the unique identifier for this tool call","type":"string"},"type":{"description":"Type is the type of tool call (always \"function\" for now)","type":"string"}},"type":"object"},"llmapi.ToolCallEvent":{"properties":{"arguments":{"type":"string"},"id":{"type":"string"},"name":{"type":"string"},"output":{"type":"string"}},"type":"object"},"llmapi.ToolCallFunction":{"description":"Function contains the function call details","properties":{"arguments":{"description":"Arguments is the JSON string of arguments to pass to the function","type":"string"},"name":{"description":"Name is the name of the function to call","type":"string"}},"type":"object"},"mcp.ToolSchema":{"properties":{"description":{"type":"string"},"name":{"type":"string"},"parameters":{}},"type":"object"},"response.ErrorResponse":{"properties":{"code":{"type":"string"},"error":{"type":"string"},"request_id":{"type":"string"},"trace_id":{"type":"string"},"type":{"type":"string"}},"type":"object"}}},
|
|
3
|
+
"info": {"description":"The Anuma API provides OpenAI-compatible REST and streaming interfaces for building AI applications with access to models from multiple providers including OpenAI, Anthropic, and more. You can create chat completions, generate embeddings, and use built-in tools, all with wallet-based authentication and on-chain credit management.","title":"Anuma Portal API","version":"1.0"},
|
|
4
|
+
"externalDocs": {"description":"","url":""},
|
|
5
|
+
"paths": {"/api/v1/admin/add-credits":{"post":{"description":"Adds credits to a user's account. The settlement worker will handle on-chain enrollment when processing requests.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.AddCreditsRequest"}}},"description":"Add credits request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.AddCreditsResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Add credits to user","tags":["Admin"]}},"/api/v1/admin/apps":{"get":{"description":"Returns all registered apps. Requires admin API key.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.ListAppsResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"List all apps","tags":["Admin"]},"post":{"description":"Creates a new app. Requires admin API key.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.CreateAppRequest"}}},"description":"Create app request","required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.AppResponse"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Create app","tags":["Admin"]}},"/api/v1/admin/apps/{app_id}/api-keys":{"get":{"description":"Returns all API keys for the specified app. Requires admin API key.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}},{"description":"App ID","in":"path","name":"app_id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.ListAPIKeysResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"List API keys for an app","tags":["Admin"]},"post":{"description":"Creates a new API key for an app. Requires admin API key.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}},{"description":"App ID","in":"path","name":"app_id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.CreateAPIKeyRequest"}}},"description":"Create API key request","required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.APIKeyWithKeyResponse"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Create API key","tags":["Admin"]}},"/api/v1/admin/apps/{app_id}/api-keys/{id}":{"delete":{"description":"Deletes an API key by ID. Requires admin API key.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}},{"description":"App ID","in":"path","name":"app_id","required":true,"schema":{"type":"integer"}},{"description":"API Key ID","in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Delete API key","tags":["Admin"]},"get":{"description":"Returns a single API key by its ID with wallet balance details. Requires admin API key.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}},{"description":"App ID","in":"path","name":"app_id","required":true,"schema":{"type":"integer"}},{"description":"API Key ID","in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.APIKeyResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get API key by ID","tags":["Admin"]},"put":{"description":"Updates an existing API key. Only provided fields are updated. Requires admin API key.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}},{"description":"App ID","in":"path","name":"app_id","required":true,"schema":{"type":"integer"}},{"description":"API Key ID","in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.UpdateAPIKeyRequest"}}},"description":"Update API key request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.APIKeyResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Update API key","tags":["Admin"]}},"/api/v1/admin/apps/{id}":{"delete":{"description":"Deletes an app by ID. Requires admin API key.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}},{"description":"App ID","in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Delete app","tags":["Admin"]},"get":{"description":"Returns a single app by its ID. Requires admin API key.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}},{"description":"App ID","in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.AppResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get app by ID","tags":["Admin"]},"put":{"description":"Updates an existing app. Only provided fields are updated. Requires admin API key.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}},{"description":"App ID","in":"path","name":"id","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.UpdateAppRequest"}}},"description":"Update app request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.AppResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Update app","tags":["Admin"]}},"/api/v1/admin/seed-apps":{"post":{"description":"Seeds apps and their API keys into the database. Uses upsert - existing apps are updated. Requires admin API key.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.SeedAppsRequest"}}},"description":"Seed apps request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.SeedAppsResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Seed apps and API keys","tags":["Admin"]}},"/api/v1/admin/subscription-tier":{"post":{"description":"Sets a user's subscription tier (basic or pro). Requires admin API key.","parameters":[{"description":"Admin API key","in":"header","name":"X-Admin-API-Key","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.SetSubscriptionTierRequest"}}},"description":"Set subscription tier request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.SetSubscriptionTierResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Set user subscription tier","tags":["Admin"]}},"/api/v1/chat/completions":{"post":{"description":"Generates a chat completion using the configured gateway. Supports streaming when stream=true.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/llmapi.ChatCompletionRequest"}}},"description":"Chat completion request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/llmapi.ChatCompletionResponse"},{"type":"string"}]}},"text/event-stream":{"schema":{"type":"string"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Model provider rate limit exceeded"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Create chat completion","tags":["Chat"]}},"/api/v1/config":{"get":{"description":"Returns public configuration including contract addresses and registered apps","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.ConfigResponse"}}},"description":"OK"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get configuration","tags":["Configuration"]}},"/api/v1/credits/balance":{"get":{"description":"Returns the credit balance and related information for the authenticated user. Optionally accepts X-Timezone header for accurate next claim time calculation.","parameters":[{"description":"IANA timezone (e.g., America/New_York)","in":"header","name":"X-Timezone","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.CreditBalanceResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Account not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"security":[{"BearerAuth":[]}],"summary":"Get credit balance","tags":["Credits"]}},"/api/v1/credits/claim-daily":{"post":{"description":"Allows authenticated users to claim free daily credits. Limited to once per calendar day in user's timezone. Requires X-Timezone header with IANA timezone (e.g., \"America/New_York\").","parameters":[{"description":"IANA timezone (e.g., America/New_York)","in":"header","name":"X-Timezone","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.ClaimDailyCreditsResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Already claimed today or invalid timezone"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Account not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"security":[{"BearerAuth":[]}],"summary":"Claim daily credits","tags":["Credits"]}},"/api/v1/credits/packs":{"get":{"description":"Returns available credit packs with prices fetched from Stripe. Pro users receive bonus credits on the same pack.","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.CreditPacksResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"security":[{"BearerAuth":[]}],"summary":"List available credit packs","tags":["Credits"]}},"/api/v1/credits/purchase":{"post":{"description":"Creates a Stripe Checkout Session for purchasing a one-time credit pack and returns the checkout URL. Pro users are charged using the Pro product with bonus credits.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.CreateCreditPackCheckoutRequest"}}},"description":"Credit pack checkout request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.CheckoutSessionResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"security":[{"BearerAuth":[]}],"summary":"Create credit pack checkout session","tags":["Credits"]}},"/api/v1/credits/sync-snag":{"post":{"description":"Queries Snag for the user's current loyalty points and converts any new points to Portal credits using the configured conversion rate.","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.SyncSnagResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Cooldown not expired"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"security":[{"BearerAuth":[]}],"summary":"Sync Snag points","tags":["Credits"]}},"/api/v1/docs/swagger.json":{"get":{"description":"Returns the OpenAPI 3.1 specification for this API","responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{},"type":"object"}}},"description":"OK"}},"summary":"Get OpenAPI specification","tags":["Documentation"]}},"/api/v1/embeddings":{"post":{"description":"Generates embeddings using the configured gateway.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/llmapi.EmbeddingRequest"}}},"description":"Embedding request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/llmapi.EmbeddingResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Model provider rate limit exceeded"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Create embeddings","tags":["Embeddings"]}},"/api/v1/models":{"get":{"description":"Returns a list of all available models from the configured gateway with optional filters. Models include modality information indicating their capabilities (e.g., llm, embedding, vision, image, audio, reasoning, code, reranker, multimodal, video).","parameters":[{"description":"Filter by provider (e.g., openai, anthropic)","in":"query","name":"provider","schema":{"type":"string"}},{"description":"Number of models to return per page","in":"query","name":"page_size","schema":{"type":"integer"}},{"description":"Token to get next page of results","in":"query","name":"page_token","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/llmapi.ModelsListResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Rate limit exceeded"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"List available models","tags":["Models"]}},"/api/v1/responses":{"post":{"description":"Generates a response using the Responses API format. Supports streaming when stream=true.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/llmapi.ResponseRequest"}}},"description":"Response request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/llmapi.ResponseResponse"},{"type":"string"}]}},"text/event-stream":{"schema":{"type":"string"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Model provider rate limit exceeded"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Create response","tags":["Chat"]}},"/api/v1/subscriptions/cancel":{"post":{"description":"Cancels the user's subscription at the end of the current billing period (cancel_at_period_end)","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.CancelSubscriptionResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"security":[{"BearerAuth":[]}],"summary":"Cancel subscription","tags":["Subscriptions"]}},"/api/v1/subscriptions/create-checkout-session":{"post":{"description":"Creates a Stripe Checkout Session for Pro subscription and returns the checkout URL","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.CreateCheckoutSessionRequest"}}},"description":"Checkout session request with redirect URLs","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.CheckoutSessionResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"security":[{"BearerAuth":[]}],"summary":"Create checkout session","tags":["Subscriptions"]}},"/api/v1/subscriptions/customer-portal":{"post":{"description":"Creates a Stripe Customer Portal session for managing subscription and returns the portal URL","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.CreateCustomerPortalRequest"}}},"description":"Customer portal request with return URL","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.CustomerPortalResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"security":[{"BearerAuth":[]}],"summary":"Create customer portal session","tags":["Subscriptions"]}},"/api/v1/subscriptions/renew":{"post":{"description":"Reactivates a subscription that was scheduled for cancellation (undoes cancel_at_period_end)","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.RenewSubscriptionResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Not Found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"security":[{"BearerAuth":[]}],"summary":"Renew subscription","tags":["Subscriptions"]}},"/api/v1/subscriptions/status":{"get":{"description":"Returns the current subscription status, plan, and billing period info","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.SubscriptionStatusResponse"}}},"description":"OK"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Unauthorized"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"security":[{"BearerAuth":[]}],"summary":"Get subscription status","tags":["Subscriptions"]}},"/api/v1/subscriptions/webhook":{"post":{"description":"Receives and processes Stripe webhook events for subscription lifecycle management","parameters":[{"description":"Stripe webhook signature","in":"header","name":"Stripe-Signature","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"}},"summary":"Handle Stripe webhook","tags":["Subscriptions"]}},"/api/v1/tasks":{"get":{"description":"Returns all active reward tasks that users can complete","responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/handlers.TaskResponse"},"type":"array"}}},"description":"OK"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Get available tasks","tags":["Credits"]}},"/api/v1/tools":{"get":{"description":"Returns a map of available MCP tool schemas indexed by tool name.","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.GetToolsResponse"}}},"description":"OK"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"List available tools","tags":["Tools"]}},"/auth/oauth/{provider}/exchange":{"post":{"description":"Exchanges an OAuth authorization code for access and refresh tokens","parameters":[{"description":"OAuth provider (google-drive, dropbox)","in":"path","name":"provider","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.ExchangeRequest"}}},"description":"Exchange request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.TokenResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Exchange authorization code for tokens","tags":["OAuth"]}},"/auth/oauth/{provider}/refresh":{"post":{"description":"Refreshes an expired access token using a refresh token","parameters":[{"description":"OAuth provider (google-drive, dropbox)","in":"path","name":"provider","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.RefreshRequest"}}},"description":"Refresh request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.TokenResponse"}}},"description":"OK"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Refresh access token","tags":["OAuth"]}},"/auth/oauth/{provider}/revoke":{"post":{"description":"Revokes an OAuth access or refresh token","parameters":[{"description":"OAuth provider (google-drive, dropbox)","in":"path","name":"provider","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.RevokeRequest"}}},"description":"Revoke request","required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{},"type":"object"}}},"description":"Empty object on success"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Bad Request"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Revoke OAuth token","tags":["OAuth"]}},"/health":{"get":{"description":"Returns the current health status of the service.","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/handlers.HealthResponse"}}},"description":"OK"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/response.ErrorResponse"}}},"description":"Internal Server Error"}},"summary":"Health check","tags":["Health"]}}},
|
|
6
|
+
"openapi": "3.1.0"
|
|
7
|
+
}
|