{"openapi":"3.1.0","info":{"title":"MeterLayer API","version":"1.0.0","description":"MeterLayer routes OpenAI-compatible chat requests through a metered gateway with budgets, logs and cost tracking."},"servers":[{"url":"https://meterlayer.io","description":"Production"}],"security":[{"projectApiKey":[]}],"paths":{"/v1/chat/completions":{"post":{"summary":"OpenAI-compatible chat completions","description":"Use this endpoint with OpenAI SDKs by setting baseURL to /v1 and apiKey to a MeterLayer project API key. Streaming is not supported.","operationId":"createOpenAiCompatibleChatCompletion","tags":["Proxy"],"parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"type":"string","maxLength":128},"description":"Optional idempotency and traceability key. Reusing the same ID for a project returns 409."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAiCompatibleChatRequest"}}}},"responses":{"200":{"description":"Provider response returned successfully.","headers":{"X-AI-Cost-Gateway-Request-Id":{"description":"Gateway request ID for finding the request in logs.","schema":{"type":"string"}},"X-AI-Cost-Gateway-Cost":{"description":"Estimated request cost in USD.","schema":{"type":"string"}},"X-AI-Cost-Gateway-RateLimit-Remaining":{"description":"Remaining requests in the current rate-limit window.","schema":{"type":"string"}},"X-AI-Cost-Gateway-RateLimit-Reset":{"description":"ISO timestamp when the current rate-limit window resets.","schema":{"type":"string","format":"date-time"}},"X-AI-Cost-Gateway-RateLimit-Backend":{"description":"Rate-limit backend used by the gateway.","schema":{"type":"string","enum":["upstash","postgres","memory"]}},"Retry-After":{"description":"Seconds until a rate-limited client should retry.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Standard remaining request count for the current rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Standard Unix timestamp in seconds when the current rate-limit window resets.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAiChatResponse"}}}},"400":{"description":"Invalid request, unsupported streaming, missing provider credential or provider setting.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid project API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Monthly project budget or plan request limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Project disabled or provider/model is not allowed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Duplicate X-Request-Id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Request body is too large.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Project rate limit exceeded.","headers":{"X-AI-Cost-Gateway-Request-Id":{"description":"Gateway request ID for finding the request in logs.","schema":{"type":"string"}},"X-AI-Cost-Gateway-RateLimit-Remaining":{"description":"Remaining requests in the current rate-limit window.","schema":{"type":"string"}},"X-AI-Cost-Gateway-RateLimit-Reset":{"description":"ISO timestamp when the current rate-limit window resets.","schema":{"type":"string","format":"date-time"}},"X-AI-Cost-Gateway-RateLimit-Backend":{"description":"Rate-limit backend used by the gateway.","schema":{"type":"string","enum":["upstash","postgres","memory"]}},"Retry-After":{"description":"Seconds until a rate-limited client should retry.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Standard remaining request count for the current rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Standard Unix timestamp in seconds when the current rate-limit window resets.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Provider returned an invalid response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"504":{"description":"Provider request timed out.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/proxy/chat":{"post":{"summary":"Gateway-native chat proxy","description":"Routes a provider-specific chat request through MeterLayer, enforcing project provider/model allow-lists, budgets and rate limits.","operationId":"createGatewayChatCompletion","tags":["Proxy"],"parameters":[{"name":"X-Request-Id","in":"header","required":false,"schema":{"type":"string","maxLength":128},"description":"Optional idempotency and traceability key. Reusing the same ID for a project returns 409."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GatewayChatRequest"}}}},"responses":{"200":{"description":"Provider response returned successfully.","headers":{"X-AI-Cost-Gateway-Request-Id":{"description":"Gateway request ID for finding the request in logs.","schema":{"type":"string"}},"X-AI-Cost-Gateway-Cost":{"description":"Estimated request cost in USD.","schema":{"type":"string"}},"X-AI-Cost-Gateway-RateLimit-Remaining":{"description":"Remaining requests in the current rate-limit window.","schema":{"type":"string"}},"X-AI-Cost-Gateway-RateLimit-Reset":{"description":"ISO timestamp when the current rate-limit window resets.","schema":{"type":"string","format":"date-time"}},"X-AI-Cost-Gateway-RateLimit-Backend":{"description":"Rate-limit backend used by the gateway.","schema":{"type":"string","enum":["upstash","postgres","memory"]}},"Retry-After":{"description":"Seconds until a rate-limited client should retry.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Standard remaining request count for the current rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Standard Unix timestamp in seconds when the current rate-limit window resets.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAiChatResponse"}}}},"400":{"description":"Invalid request, unsupported streaming, missing provider credential or provider setting.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid project API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Monthly project budget or plan request limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Project disabled or provider/model is not allowed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Duplicate X-Request-Id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"413":{"description":"Request body is too large.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Project rate limit exceeded.","headers":{"X-AI-Cost-Gateway-Request-Id":{"description":"Gateway request ID for finding the request in logs.","schema":{"type":"string"}},"X-AI-Cost-Gateway-RateLimit-Remaining":{"description":"Remaining requests in the current rate-limit window.","schema":{"type":"string"}},"X-AI-Cost-Gateway-RateLimit-Reset":{"description":"ISO timestamp when the current rate-limit window resets.","schema":{"type":"string","format":"date-time"}},"X-AI-Cost-Gateway-RateLimit-Backend":{"description":"Rate-limit backend used by the gateway.","schema":{"type":"string","enum":["upstash","postgres","memory"]}},"Retry-After":{"description":"Seconds until a rate-limited client should retry.","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Standard remaining request count for the current rate-limit window.","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"Standard Unix timestamp in seconds when the current rate-limit window resets.","schema":{"type":"string"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Provider returned an invalid response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"504":{"description":"Provider request timed out.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/health/live":{"get":{"summary":"Liveness check","operationId":"getLiveness","tags":["Health"],"security":[],"responses":{"200":{"description":"Application process is responding.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LivenessResponse"}}}}}}},"/api/health/ready":{"get":{"summary":"Readiness check","operationId":"getReadiness","tags":["Health"],"security":[],"responses":{"200":{"description":"Application is ready.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadinessResponse"}}}},"503":{"description":"Application is not ready.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadinessResponse"}}}}}}}},"components":{"securitySchemes":{"projectApiKey":{"type":"http","scheme":"bearer","bearerFormat":"MeterLayer project API key"}},"schemas":{"ChatMessage":{"type":"object","required":["role","content"],"properties":{"role":{"type":"string","enum":["system","user","assistant"]},"content":{"type":"string","minLength":1,"maxLength":32000}}},"Metadata":{"type":"object","additionalProperties":true,"description":"Optional metadata stored with request logs. Recommended keys include feature, userId, customerId and tenantId."},"GatewayChatRequest":{"type":"object","required":["provider","model","messages"],"properties":{"provider":{"type":"string","enum":["openai","anthropic","gemini"],"description":"MVP forwards OpenAI requests."},"model":{"type":"string","minLength":1,"example":"gpt-4o-mini"},"messages":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/ChatMessage"}},"maxOutputTokens":{"type":"integer","minimum":1,"description":"Defaults server-side when omitted."},"temperature":{"type":"number","minimum":0,"maximum":2},"topP":{"type":"number","minimum":0,"maximum":1},"presencePenalty":{"type":"number","minimum":-2,"maximum":2},"frequencyPenalty":{"type":"number","minimum":-2,"maximum":2},"stop":{"oneOf":[{"type":"string"},{"type":"array","minItems":1,"maxItems":4,"items":{"type":"string"}}]},"metadata":{"$ref":"#/components/schemas/Metadata"}}},"OpenAiCompatibleChatRequest":{"type":"object","required":["model","messages"],"properties":{"model":{"type":"string","minLength":1,"example":"gpt-4o-mini"},"messages":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/ChatMessage"}},"max_tokens":{"type":"integer","minimum":1},"temperature":{"type":"number","minimum":0,"maximum":2},"top_p":{"type":"number","minimum":0,"maximum":1},"presence_penalty":{"type":"number","minimum":-2,"maximum":2},"frequency_penalty":{"type":"number","minimum":-2,"maximum":2},"stop":{"oneOf":[{"type":"string"},{"type":"array","minItems":1,"maxItems":4,"items":{"type":"string"}}]},"stream":{"type":"boolean","description":"Must be false or omitted. Streaming is not supported yet."},"metadata":{"$ref":"#/components/schemas/Metadata"}}},"OpenAiChatResponse":{"type":"object","additionalProperties":true,"description":"Provider response body. For OpenAI this is the chat completions response shape."},"ErrorResponse":{"type":"object","properties":{"error":{"type":"string"},"requestId":{"type":"string"},"details":{"type":"object","additionalProperties":true}}},"OpenAiCompatibleError":{"type":"object","properties":{"error":{"type":"object","required":["message","type"],"properties":{"message":{"type":"string"},"type":{"type":"string"}}}}},"LivenessResponse":{"type":"object","required":["ok","service","status","checkedAt"],"properties":{"ok":{"type":"boolean"},"service":{"type":"string","enum":["ai-cost-gateway"]},"status":{"type":"string","enum":["live"]},"checkedAt":{"type":"string","format":"date-time"}}},"ReadinessResponse":{"type":"object","required":["ok","service","status","checks","checkedAt"],"properties":{"ok":{"type":"boolean"},"service":{"type":"string","enum":["ai-cost-gateway"]},"status":{"type":"string","enum":["ready","not_ready"]},"checks":{"type":"object","additionalProperties":true},"checkedAt":{"type":"string","format":"date-time"}}}}}}