ModelFaucet API Specification
Version: v1.1 Source GA Auth Hardening Date: 2026-06-18
1. Conventions
Base URLs:
Control API: https://api.modelfaucet.dev
Gateway API: https://gateway.modelfaucet.dev/v1
Local Bridge: http://127.0.0.1:8787Auth:
Developer bootstrap/admin endpoints: Bearer mf_admin_xxx
Developer scoped endpoints: Bearer mf_dev_xxx
End-user/session endpoints: Bearer mf_sess_xxx
Gateway endpoints: Bearer mf_sess_xxxCORS:
Development may use permissive CORS for local demos.
Production API deployments must set API_CORS_ORIGINS.
Production Gateway deployments must set GATEWAY_CORS_ORIGINS.
Production CORS origins must be explicit http/https origins and cannot be *.Provider URL safety:
Cloud-routed provider base URLs reject localhost, private LAN, link-local,
carrier NAT, metadata hostnames, private IPv6, and IPv4-mapped private hosts.
Local Bridge is the only local/LAN model path and runs inside the user boundary.Errors:
{
"error": {
"code": "insufficient_balance",
"message": "The end user wallet does not have enough credits.",
"request_id": "req_xxx"
}
}Common error codes:
invalid_request
invalid_session
expired_session
forbidden
invalid_app
feature_not_found
no_available_route
insufficient_balance
budget_exceeded
rate_limited
provider_error
local_bridge_unavailable
secret_validation_failed2. Session API
GET /ready
Readiness endpoint for API container probes.
GET /metrics
Prometheus-style text metrics for API request totals, duration sums, and rate-limit counters.
POST /v1/sessions
Creates a short-lived session token for an app end user.
Request:
{
"public_app_id": "app_pub_demo",
"external_user_id": "user_123",
"feature_key": "customer_reply",
"metadata": {
"plan": "free",
"locale": "zh-CN"
}
}Response:
{
"session_token": "mf_sess_abc",
"expires_in": 3600,
"gateway_base_url": "https://gateway.modelfaucet.dev/v1",
"available_modes": ["platform", "byok", "local"],
"wallet_balance_usd": "10.00000000"
}Rules:
- external_user_id must be hashed before storage.
- session_token must be stored hashed.
- token TTL defaults to 3600 seconds.
- public_app_id is not a secret.3. Gateway API
GET /ready
Readiness endpoint for Gateway container probes.
GET /metrics
Prometheus-style text metrics for Gateway request totals, duration sums, and rate-limit counters.
GET /health/providers
Returns sanitized provider health information for operator checks. It must not include raw provider keys, Authorization headers, or secret-bearing URLs.
Example response:
{
"ok": true,
"providers": [
{
"ok": true,
"provider": "litellm",
"statusCode": 200,
"latencyMs": 12
}
]
}POST /v1/chat/completions
OpenAI-compatible chat completions endpoint.
Headers:
Authorization: Bearer mf_sess_abc
Content-Type: application/jsonRequest:
{
"model": "auto:customer_reply",
"messages": [
{
"role": "user",
"content": "客户说物流太慢,要求退款,帮我回复。"
}
],
"stream": false,
"metadata": {
"feature_key": "customer_reply"
}
}Response:
{
"id": "chatcmpl_mf_123",
"object": "chat.completion",
"created": 1781635200,
"model": "auto:customer_reply",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "您好,非常抱歉这次物流延误给您带来不便..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 128,
"completion_tokens": 96,
"total_tokens": 224
},
"modelfaucet": {
"request_id": "req_abc",
"route_mode": "platform",
"feature_key": "customer_reply",
"estimated_price_usd": "0.00123400"
}
}stream: true is currently rejected with invalid_request until streaming ledger accounting is implemented.
4. BYOK API
POST /v1/user/provider-keys
Adds an end-user provider key.
Auth: mf_sess_xxx
Request:
{
"provider": "openai",
"api_key": "sk-...",
"base_url": "https://api.openai.com/v1",
"models_allowed": ["gpt-4.1-mini"],
"budget_limit_usd": "20.00",
"priority": 1,
"fallback_to_platform": false
}Response:
{
"id": "cred_123",
"provider": "openai",
"base_url": "https://api.openai.com/v1",
"masked": "sk-...abcd",
"status": "active",
"models_allowed": ["gpt-4.1-mini"],
"priority": 1,
"fallback_to_platform": false
}Rules:
- Validate the key before storing if provider supports cheap validation.
- Encrypt before persistence.
- Never return raw api_key.
- Log audit event.GET /v1/user/provider-keys
Response:
{
"items": [
{
"id": "cred_123",
"provider": "openai",
"masked": "sk-...abcd",
"status": "active",
"priority": 1
}
]
}DELETE /v1/user/provider-keys/:id
Deletes or disables a key.
Response:
{ "ok": true }5. Local Endpoint API
POST /v1/local/endpoints
Registers a local endpoint preference. The cloud API stores metadata only; it must not directly access the LAN URL.
Request:
{
"name": "ollama-qwen",
"base_url": "http://localhost:11434/v1",
"provider": "openai_compatible",
"models": ["qwen2.5:7b"],
"mode": "local_bridge"
}Response:
{
"id": "local_ep_123",
"name": "ollama-qwen",
"status": "registered"
}Validation:
- For cloud API, reject any attempt to fetch private network URLs.
- For Local Bridge, allow localhost and LAN URLs if configured by the user.6. Local Bridge API
GET /health
Response:
{
"ok": true,
"version": "0.5.0",
"listening": "127.0.0.1:8787"
}GET /diagnostics
Response:
{
"ok": true,
"version": "0.5.0",
"listening": "127.0.0.1:8787",
"upstream_base_url": "http://127.0.0.1:11434/v1",
"upstream_reachable": true,
"models_count": 1,
"checks": [
{ "name": "loopback_bind", "ok": true },
{ "name": "upstream_models", "ok": true, "detail": "1 models reported" }
]
}The diagnostics response does not include upstream API keys.
GET /models
Response:
{
"items": [
{
"id": "ollama:qwen2.5:7b",
"provider": "ollama",
"endpoint_id": "ollama",
"capabilities": ["chat", "json"]
}
]
}POST /v1/chat/completions
OpenAI-compatible proxy to local model.
Request:
{
"model": "ollama:qwen2.5:7b",
"messages": [
{ "role": "user", "content": "总结这段文字。" }
]
}Response: OpenAI-compatible.
POST /usage/report
Bridge reports metadata to cloud ledger.
Request:
{
"request_id": "req_local_123",
"app_id": "app_123",
"end_user_id_hash": "hash_xxx",
"feature_key": "summarize",
"route_mode": "local",
"provider": "ollama",
"model": "qwen2.5:7b",
"input_tokens": 1000,
"output_tokens": 200,
"created_at": "2026-06-17T00:00:00Z"
}7. Developer Console API
Developer-console routes accept either the bootstrap DEVELOPER_ADMIN_TOKEN or a scoped mf_dev_... developer API token. Production developer access should use scoped developer API tokens. The bootstrap admin token is for operator-only setup and compatibility.
Provider API keys are managed only through explicit provider-key endpoints; app, feature, operations, wallet, payout, and audit responses never include raw provider secrets.
POST /v1/developer/tokens
Creates a scoped developer API token. The raw token is returned only once. The database stores only a token hash and token prefix.
Required scope: developer:tokens:write.
Request:
{
"developer_email": "dev@example.com",
"name": "production console",
"scopes": [
"developer:apps:read",
"developer:apps:write",
"developer:features:read",
"developer:features:write",
"developer:operations:read",
"developer:provider_keys:read",
"developer:provider_keys:write"
],
"expires_at": "2026-12-31T00:00:00.000Z"
}Response:
{
"token": "mf_dev_example",
"item": {
"id": "11111111-1111-4111-8111-111111111111",
"developer_id": "22222222-2222-4222-8222-222222222222",
"developer_name": "Demo Developer",
"developer_email": "dev@example.com",
"name": "production console",
"token_prefix": "mf_dev_example",
"scopes": ["developer:apps:read"],
"status": "active",
"created_at": "2026-06-18T00:00:00.000Z",
"updated_at": "2026-06-18T00:00:00.000Z"
}
}GET /v1/developer/tokens
Lists token metadata. It never returns raw tokens or token hashes.
Required scope: developer:tokens:read.
Response:
{
"items": [
{
"id": "11111111-1111-4111-8111-111111111111",
"name": "production console",
"token_prefix": "mf_dev_abcd1234",
"scopes": ["developer:apps:read"],
"status": "active"
}
]
}DELETE /v1/developer/tokens/:tokenId
Revokes a developer API token.
Required scope: developer:tokens:write.
Response:
{
"ok": true
}GET /v1/developer/apps
Required scope: developer:apps:read.
Response:
{
"items": [
{
"public_app_id": "app_pub_demo",
"name": "CRM Demo",
"vertical": "crm",
"default_revenue_share_bps": 4000,
"status": "active",
"developer_id": "22222222-2222-4222-8222-222222222222",
"developer_name": "Demo Developer",
"developer_email": "dev@example.com",
"created_at": "2026-06-17T00:00:00.000Z",
"updated_at": "2026-06-17T00:00:00.000Z"
}
]
}POST /v1/developer/apps
Required scope: developer:apps:write.
Request:
{
"public_app_id": "app_pub_support",
"name": "Support Console",
"vertical": "support",
"default_revenue_share_bps": 4200,
"status": "active"
}Response: app summary.
PATCH /v1/developer/apps/:publicAppId
Required scope: developer:apps:write.
Request:
{
"name": "Support Console",
"default_revenue_share_bps": 4500,
"status": "active"
}Response: app summary.
DELETE /v1/developer/apps/:publicAppId
Archives an app by setting status = disabled.
Required scope: developer:apps:write.
Response: app summary.
GET /v1/developer/apps/:publicAppId/features
Required scope: developer:features:read.
Response:
{
"items": [
{
"id": "11111111-1111-4111-8111-111111111111",
"public_app_id": "app_pub_demo",
"feature_key": "customer_reply",
"display_name": "Customer reply",
"policy": {
"route_preference": ["local", "developer_key", "platform_pool"]
},
"pricing": {
"mode": "usage_markup",
"markup_percent": 30,
"channel_share_bps": 4000
},
"created_at": "2026-06-17T00:00:00.000Z",
"updated_at": "2026-06-17T00:00:00.000Z"
}
]
}POST /v1/developer/apps/:publicAppId/features
Required scope: developer:features:write.
Request:
{
"feature_key": "customer_reply",
"display_name": "客户回复生成",
"policy": {
"route_preference": ["local", "end_user_byok", "developer_key", "platform_pool"],
"privacy": "redact_pii_before_cloud",
"model_policy": "cheapest_sufficient"
},
"pricing": {
"mode": "usage_markup",
"markup_percent": 30,
"channel_share_bps": 4000
}
}Response: feature summary.
PATCH /v1/developer/apps/:publicAppId/features/:featureKey
Required scope: developer:features:write.
Request:
{
"display_name": "Customer reply",
"pricing": {
"mode": "usage_markup",
"markup_percent": 35,
"channel_share_bps": 4200
}
}Response: feature summary.
DELETE /v1/developer/apps/:publicAppId/features/:featureKey
Required scope: developer:features:write.
Response:
{
"ok": true
}GET /v1/developer/operations
Required scope: developer:operations:read.
Response:
{
"wallets": [
{
"id": "33333333-3333-4333-8333-333333333333",
"owner_scope": "developer",
"owner_id": "22222222-2222-4222-8222-222222222222",
"owner_name": "Demo Developer",
"balance_usd": "1.25000000",
"updated_at": "2026-06-17T00:00:00.000Z"
}
],
"topups": [],
"payouts": [],
"audit_logs": []
}POST /v1/developer/provider-keys
Stores an encrypted developer-owned provider key for an owned app. Raw provider keys are accepted only in this server endpoint, encrypted before storage, and never returned.
Required scope: developer:provider_keys:write.
Request:
{
"public_app_id": "app_pub_demo",
"provider": "openai",
"api_key": "sk-placeholder",
"base_url": "https://api.openai.com/v1",
"models_allowed": ["gpt-4.1-mini"],
"priority": 1,
"fallback_to_platform": false
}Response: masked provider-key summary.
GET /v1/developer/provider-keys
Lists masked developer provider keys for an owned app.
Required scope: developer:provider_keys:read.
Query:
public_app_id=app_pub_demoResponse:
{
"items": [
{
"id": "22222222-2222-4222-8222-222222222222",
"provider": "openai",
"masked": "sk-...abcd",
"status": "active",
"models_allowed": ["gpt-4.1-mini"],
"priority": 1,
"fallback_to_platform": false
}
]
}DELETE /v1/developer/provider-keys/:id
Disables a developer provider key owned by the authenticated developer.
Required scope: developer:provider_keys:write.
Response:
{
"ok": true
}8. Billing And Settlement API
All endpoints in this section require the admin bearer token. CSV and reconciliation responses never include raw provider keys or BYOK secrets.
POST /v1/admin/payouts/run-mock
Creates pending payout records for developer wallets that meet the configured threshold.
Request:
{
"threshold_usd": "1.00000000"
}Response:
{
"items": [
{
"id": "11111111-1111-4111-8111-111111111111",
"developer_id": "22222222-2222-4222-8222-222222222222",
"amount_usd": "12.50000000",
"status": "pending",
"provider": "mock"
}
]
}POST /v1/admin/payouts/:id/approve
Moves a pending payout to processing. This approval gate is required before a payout can be marked paid.
Request:
{
"operator_note": "reviewed against ledger reconciliation"
}Response: payout summary.
POST /v1/admin/payouts/:id/mark-paid
Marks an approved payout paid and debits the developer wallet. Payouts that have not passed approve are rejected.
Response: payout summary.
GET /v1/admin/reconciliation/ledger
Reconstructs wallet balances from ledger_entries and compares them with the current wallets.balance_usd value.
Response:
{
"generated_at": "2026-06-18T00:00:00.000Z",
"summary": {
"wallet_count": 4,
"balanced_count": 4,
"mismatch_count": 0
},
"items": [
{
"wallet_id": "33333333-3333-4333-8333-333333333333",
"owner_scope": "end_user",
"owner_id": "44444444-4444-4444-8444-444444444444",
"wallet_balance_usd": "9.99870000",
"ledger_balance_usd": "9.99870000",
"delta_usd": "0.00000000",
"status": "balanced"
}
]
}POST /v1/admin/wallets/:id/adjustments
Records an explicit adjustment, refund, or chargeback against a wallet. The operation updates the wallet balance, writes a ledger entry, and creates an audit log.
Request:
{
"kind": "refund",
"direction": "credit",
"amount_usd": "2.50000000",
"reason": "test-mode refund",
"idempotency_key": "refund-demo-001"
}Response:
{
"id": "55555555-5555-4555-8555-555555555555",
"wallet_id": "33333333-3333-4333-8333-333333333333",
"kind": "refund",
"direction": "credit",
"amount_usd": "2.50000000",
"status": "applied",
"reason": "test-mode refund",
"idempotency_key": "refund-demo-001",
"wallet_balance_usd": "12.49870000"
}GET /v1/admin/reports/usage.csv
Returns a CSV export with request-level usage, token counts, route mode, provider, cost, retail price, channel revenue, and platform revenue.
GET /v1/admin/reports/revenue.csv
Returns a CSV export with app/developer-level revenue totals.
GET /v1/admin/reports/payouts.csv
Returns a CSV export with payout period records and review status.
9. Usage and Revenue API
GET /v1/apps/:id/usage
Response:
{
"items": [
{
"request_id": "req_abc",
"feature_key": "customer_reply",
"route_mode": "platform",
"provider": "openrouter",
"model": "auto-text",
"input_tokens": 128,
"output_tokens": 96,
"retail_price_usd": "0.00123400",
"channel_revenue_usd": "0.00012300",
"created_at": "2026-06-17T00:00:00Z"
}
]
}GET /v1/developers/:id/revenue
Response:
{
"developer_id": "dev_123",
"wallet_balance_usd": "12.34560000",
"pending_payout_usd": "10.00000000",
"lifetime_revenue_usd": "34.56780000"
}