What is GreenOps Studio AI?
GreenOps Studio AI is an AI Control Plane Enterprise and Edge-Native AI Proxy: an OpenAI-compatible LLM Security Gateway and AI FinOps Platform on Cloudflare Workers. It applies semantic caching, smart Flash routing, RAG token compression, FinOps telemetry, and PII Pre-Masking AES-256-GCM before Gemini, OpenAI, or a local GPU — with an AI Audit Trail SHA-256 for EU AI Act Compliance Software duties.
Introduction & architecture
GreenOps AI is an OpenAI-compatible LLM gateway on Cloudflare Workers. Traffic uses native TLS 1.3 (TLS 1.2 fallback) at the edge. Every chat request is Bearer-authenticated, heuristically sanitized, AES-256-GCM PII-sealed, optionally served from an advanced semantic KV cache, then smart-routed to Flash, a local GPU, Gemini Pro, or OpenAI — with a SHA-256 notary block on each completion.
Edge Worker
TypeScript proxy at the Cloudflare PoP with native TLS 1.3 (TLS 1.2 fallback). OpenAI-compatible POST /v1/chat/completions on the EU edge.
Advanced semantic cache
Multi-Tier Semantic Cache on GREENOPS_CACHE: exact/canonical/bag fingerprints, then edge cosine ≥ 95%, then LSH/Jaccard neighbor (≥ 0.65). HIT replies (including Studio SSE) skip the GPU and cost $0 / 0 g CO₂. Match type is X-GreenOps-Cache-Match.
Smart routing
Light prompts go to Flash/mini/local GPU. Pro/4o stay for high-complexity work. Outcome is X-GreenOps-Route.
RAG compression
PDF/TXT in context_documents are cleaned, reranked and capped (~28% / 280 tokens) before the LLM payload.
PII masking
On the EU edge (gcp:europe-west1), emails, cards, phones and tax IDs are AES-256-GCM sealed in WASM and pre-masked to placeholders before any Gemini/OpenAI call. Count is returned on X-PII-Redacted-Count and X-GreenOps-Sealed.
SHA-256 notary ledger
Every LLM completion and IDP ingest/answer appends a block to a private SHA-256 chain (prevHash→hash, schema v4). Header X-Audit-Hash exposes the head; GET /v1/audit/verify proves integrity for corporate security audits (EU AI Act).
FinOps / ESG
Response headers expose USD saved, CO₂ avoided, cache HIT/MISS, route, quota remaining and user tier.
Heuristic sanitization
The edge Worker scores incoming prompts in real time, rewrites injection, exploit and secret-leakage spans, then forwards the corrected text. Residual high-confidence attacks return 403 with X-GreenOps-Sanitize: blocked.
- Cold path (MISS): compress RAG + WASM trim, smart-route the model, call the LLM, write KV, emit FinOps headers.
- Hot path (HIT): KV/memory read — typically tens of milliseconds. Works with
stream: true(SSE synthesized from cache). - Hybrid failover: cloud outage or
X-GreenOps-Confidentialityhedges instantly to Ollama/GPU; chat continuity preserved. - Chat history: each completion is appended to Cloudflare KV
GREENOPS_CHATSfor the signed-in user.
FinOps stack: cache, routing, RAG
Three levers cut spend before a paid model is called. All four plans (Free, Pro, Business, Enterprise) get advanced semantic cache. Paid plans add longer TTL, failover, and higher rate limits.
Smart model routing
The edge micro-classifier scores complexity (length, reasoning verbs, RAG). Below the premium gate (~0.82, recalibrated by the EU monitor) Pro/4o are downgraded to Flash, mini, or local-llama3-8b. Above the gate, Flash/mini are promoted to the preferred flagship (Gemini Pro / GPT-4o). Outcome is X-GreenOps-Route + X-GreenOps-Cascade.
X-GreenOps-Route: flash— light task on Gemini Flash or GPT-4o-mini (after semantic-cache miss).X-GreenOps-Route: local— local GPU / Ollama when reachable.X-GreenOps-Route: premium— high-complexity / reasoning → flagship (including auto-promotion from Flash).X-GreenOps-Route: green— non-urgent small-tier pinned to the greener EU grid.X-GreenOps-Cache-Match—exact·bag·cosine·neighbor·vectorize(HIT = $0 tokens).
European carbon-aware routing
Non-urgent work is steered to the greenest reachable datacenter using live EU grid intensity.
EU edge performance monitor
Background telemetry recalibrates smart routing from empirical latency, accuracy and cost.
RAG token compression
Send PDF/TXT excerpts as context_documents (string or string[]). The Worker always strips page numbers, drops near-duplicates, reranks against the user query (relevance ≥ 0.32) and caps the injected context at 28% of original tokens or 280 tokens. Studio attachments take the same path.
Intelligent Document Processing (IDP)
Zero-export document pipeline for banks, insurers, and public sector: native PDF/TXT parsing on the Worker, tenant-scoped vector archive (VECTORIZE_DOCS), extractive answers without calling cloud LLMs, and SHA-256 notary linkage (schemaVersion: 4, field idp.documentSha256, header X-Audit-Hash).
IDP endpoints
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/idp/ingest | Parse PDF/TXT (Base64 or text), PII-seal, chunk + Vectorize upsert · X-GreenOps-Document-Hash |
| GET | /v1/idp/documents | List tenant archive metadata (no full plaintext dump) |
| POST | /v1/idp/query | Vector / Jaccard retrieval over corporate chunks |
| POST | /v1/idp/answer | Extractive grounded answer + citations · audit idp_answer |
Example: ingest + answer
curl -X POST https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/idp/ingest \
-H "Authorization: Bearer GreenOpsKey123" -H "X-User-Role: User" \
-H "Content-Type: application/json" \
-d '{"filename":"nda.txt","text":"ARTICOLO 1 … zero-export …"}'
curl -X POST https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/idp/answer \
-H "Authorization: Bearer GreenOpsKey123" -H "X-User-Role: User" \
-H "Content-Type: application/json" \
-d '{"query":"clausola zero-export","topK":4}'
Every ingest/query/answer appends a notarized audit block (provider: idp, signed idp metadata, blockHeight/chainId) with response header X-Audit-Hash. Compliance verifies integrity via GET /v1/audit/logs and GET /v1/audit/verify.
FinOps headers & quotas
Response headers
| Header | Meaning |
|---|---|
X-Cache-Status | HIT / MISS / BYPASS. HIT is valid for streamed Studio replies. |
X-GreenOps-Cache-Match | memory / exact / canonical / bag / cosine (≥95%) / neighbor. |
X-GreenOps-Route | flash · local · premium · requested |
X-Fallback-Triggered | true when 429/5xx on the primary forced a secondary provider. |
X-GreenOps-Cost-Saved-USD | USD avoided (cache HIT + WASM trim + RAG compression). |
X-GreenOps-CO2-Saved-G | Grams of CO₂ avoided. |
X-RateLimit-Limit | Window quota: 1000 (Free/day), 100000 (Pro/month), 1000000 (Business/month), unlimited (Enterprise). |
X-RateLimit-Remaining | Requests left, or unlimited on Enterprise. |
X-RateLimit-Window | day · month · unlimited |
X-PII-Redacted-Count | PII items masked before upstream. |
X-GreenOps-Sanitize | clean / corrected / blocked. Corrected prompts are rewritten then forwarded. |
X-GreenOps-Chat-Id | KV conversation UUID for the saved turn. |
Rate limits by plan
| Plan | Window | Burst / min | Cache TTL |
|---|---|---|---|
| Free | 1,000 req/day | 30 | 24 hours · semantic cache on |
| Pro | 100,000 req/month | 1,000 | 30 days · failover OpenAI |
| Business | 1,000,000 req/month | 5,000 | 30 days · ESG/FinOps export |
| Enterprise | Unlimited | 20,000 | 30 days · dedicated footprint |
Authentication & setup
Protected routes require gateway authentication via Authorization: Bearer <key> (preferred) or the native header X-Proxy-Auth. Studio JWT sessions also resolve to the same tenant key. All client↔edge traffic is carried over Cloudflare TLS 1.3 (secure TLS 1.2 fallback). For local and sandbox testing use the bootstrap key GreenOpsKey123.
Admin whitelist (server-side)
Admin privileges are not granted by the client header alone. The Worker elevates accounts whose email is listed in ADMIN_EMAILS (default includes info@copycomp.it), or when the master bootstrap key is presented with X-User-Role: Admin. This unlocks /v1/admin/users, the legal pack, air-gap controls, and analytics reset — Designed & Engineered in Italy for enterprise least-privilege.
Quick test key
Send Authorization: Bearer GreenOpsKey123 on chat, analytics and audit calls. Paid plans receive a gop_pro_… key after Stripe checkout. Provider keys (Gemini/OpenAI) are optional when the Worker already has server-side credentials.
| Header | Required | Meaning |
|---|---|---|
Authorization | Yes* | Bearer token. Example: Bearer GreenOpsKey123. |
X-Proxy-Auth | Yes* | Equivalent gateway key if you do not send Authorization. |
X-User-Role | No | User (default), FinOps, Compliance, Admin. |
X-User-Gemini-Key | No | Bring-your-own Gemini key. |
X-User-OpenAI-Key | No | Bring-your-own OpenAI key. |
X-Local-LLM-Url | No | OpenAI-compatible local runtime (Ollama / GPU). |
X-Webhook-Url | No | Slack / Teams webhook. POSTed asynchronously via ctx.waitUntil with FinOps (cost, tokens) and ESG (carbon offset) fields. |
X-Chat-Id | No | Existing conversation UUID. Completions also accept body chat_id. |
Copy-ready authentication
Send Authorization: Bearer <TOKEN> on every protected call. The snippets below POST a JSON chat body to /v1/chat/completions. Replace GreenOpsKey123 with your provisioned key in production.
Endpoints & API reference
Proxy URL: https://greenops-ai-proxy.copycomp-ai.workers.dev
https://greenops-ai-proxy.copycomp-ai.workers.dev
https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/chat/completions
OpenAPI explorer
Interactive live OpenAPI 3.0: expand any operation, set Bearer + X-User-Role, then Try it out against the production gateway.
Cloudflare Workers (EU edge) and mandatory PII masking guarantee full alignment with European data-protection standards (GDPR / EU AI Act). Response headers such as X-Export-Reports and X-User-Role document export eligibility and RBAC. Made in Italy: quality, ethics, transparency.
Loading specification…
| Method | Path | Auth | Purpose |
|---|---|---|---|
| POST | /v1/chat/completions | Yes + X-User-Role | OpenAI-compatible chat (JSON or SSE). Semantic cache, smart routing, RAG, PII seal. KV persist. |
| GET | /v1/account/quota | Yes | Remaining quota for the current plan (day/month/unlimited) |
| GET | /v1/chats | Yes + X-User-Role | List saved conversations (User / Admin) |
| POST | /v1/chats | Yes + X-User-Role | Create an empty chat |
| GET | /v1/chats/:id | Yes + X-User-Role | Load messages of one conversation |
| DELETE | /v1/chats/:id | Yes + X-User-Role | Delete a conversation from KV |
| GET | /health | No | Uptime probe: status, timestamp, KV semantic cache |
| GET | /v1/health | No | KV cache + analytics, Gemini / OpenAI readiness |
| GET | /v1/monitoring/models | Yes + X-User-Role | EU edge EMA latency/accuracy/cost + smart-routing policy |
| GET | /v1/analytics | Yes + X-User-Role | FinOps / ESG totals. Response X-Export-Reports: true on Business/Enterprise |
| POST | /v1/analytics/reset | Yes + Admin | Reset KV analytics totals |
| GET | /v1/reports/monthly | FinOps/Admin + X-Export-Reports | CSRD/ESG monthly pack JSON/CSV/HTML. Business/Enterprise |
| GET | /v1/audit/logs | Compliance / Admin | EU AI Act SHA-256 notary chain (schema v4) · X-Audit-Hash |
| GET/POST | /v1/audit/verify | Compliance / Admin | Notary integrity check · tamperEvidence · PQC status |
| GET/POST | /v1/security/pqc | Compliance / Admin | ML-KEM-768 hybrid + Cloudflare PQ TLS profile |
| GET | /v1/esg/ledger | Compliance / FinOps / Admin | CSRD ESRS E1 credits ledger · X-Export-Reports: csrd-ledger |
| GET | /v1/esg/savings-chain | Yes + X-User-Role | SHA-256 CO₂/energy savings chain (cache + routing + RAG) |
| GET | /v1/finops/payback | FinOps / Admin | License payback / ROI · X-Export-Reports: finops-payback |
| GET | /v1/admin/legal-pack | Admin | MSA + GDPR/EU AI Act attestation · X-Export-Reports: admin-legal-pack |
| GET/POST | /v1/sovereignty/air-gap | Admin (GET also Compliance) | National air-gap kill-switch — blocks cloud egress, 100% local Ollama/cluster · X-GreenOps-Air-Gap |
| POST | /v1/webhooks/stripe | Stripe-Signature | Checkout/invoice → Pro, Business or Enterprise; cancel/unpaid → deactivate |
| POST | /api/webhooks/stripe | Stripe-Signature | Alias of /v1/webhooks/stripe |
| GET | /v1/openapi.json | No | OpenAPI 3.0.3 — drives this interactive explorer |
| GET | /v1/openapi/validation | No | Enterprise/compliance OpenAPI validation report (audit, ledger, RBAC, export) |
Chat Completions
POST JSON with model + messages. Optional context_documents are always compressed before the LLM. Every sample sends Authorization: Bearer <TOKEN>. Testing uses GreenOpsKey123. Replies persist to KV and return X-GreenOps-Chat-Id, plus X-GreenOps-Route and cache/quota headers. stream: true still participates in the semantic cache.
Copy a language below. Each block is a ready-to-run POST to /v1/chat/completions.
Chat history & sessions
Studio conversations are stored on Cloudflare KV (GREENOPS_CHATS, fallback GREENOPS_CACHE). Every record has a mandatory user_id foreign key: list/get/delete/save queries only return rows for the signed-in user (WHERE user_id = current_user.id). History requires login. Pass X-Chat-Id (or body chat_id) to continue a session; the response header X-GreenOps-Chat-Id is the conversation to reuse.
- Per-key isolation: KV keys never store the Bearer secret in plaintext.
- Limits: 80 chats per user, 200 messages per chat. Title is taken from the first user prompt.
- Studio sidebar calls
GET /v1/chatsandGET /v1/chats/:idwith the same Bearer token as completions. - Bind
GREENOPS_CHATSinwrangler.toml. Completions still persist if the binding falls back toGREENOPS_CACHE.
| Method | Path | Auth | Purpose |
|---|---|---|---|
| POST | /v1/chat/completions | Yes | Saves the turn; returns X-GreenOps-Chat-Id |
| GET | /v1/chats | Yes | { chats: [{ id, title, updatedAt, preview, messageCount }] } |
| POST | /v1/chats | Yes | Create empty chat { title? } → 201 |
| GET | /v1/chats/:id | Yes | { id, title, messages: [{ role, content, at }] } |
| DELETE | /v1/chats/:id | Yes | { deleted: true, id } |
Copy a session example: complete a chat, list history, create an empty chat, reload messages, then delete. Always send Authorization: Bearer <TOKEN>.
# 1) First turn — persist a new chat (read X-GreenOps-Chat-Id from the response)
curl -D - https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer GreenOpsKey123" \
-d '{
"model": "gemini-3.5-flash",
"messages": [{"role": "user", "content": "Hello GreenOps"}]
}'
# 2) Continue the same session
curl https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer GreenOpsKey123" \
-H "X-Chat-Id: CHAT_ID" \
-d '{
"model": "gemini-3.5-flash",
"messages": [
{"role": "user", "content": "Hello GreenOps"},
{"role": "assistant", "content": "Hi"},
{"role": "user", "content": "Continue"}
]
}'
# 3) List, load, create empty, delete
curl https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/chats \
-H "Authorization: Bearer GreenOpsKey123"
curl https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/chats/CHAT_ID \
-H "Authorization: Bearer GreenOpsKey123"
curl -X POST https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/chats \
-H "Authorization: Bearer GreenOpsKey123" \
-H "Content-Type: application/json" \
-d '{"title":"New chat"}'
curl -X DELETE https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/chats/CHAT_ID \
-H "Authorization: Bearer GreenOpsKey123"
import requests
PROXY = "https://greenops-ai-proxy.copycomp-ai.workers.dev"
TOKEN = "GreenOpsKey123"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {TOKEN}",
}
def complete(messages, chat_id=None):
h = dict(headers)
if chat_id:
h["X-Chat-Id"] = chat_id
res = requests.post(
f"{PROXY}/v1/chat/completions",
headers=h,
json={"model": "gemini-3.5-flash", "messages": messages},
timeout=60,
)
res.raise_for_status()
return res.headers.get("X-GreenOps-Chat-Id"), res.json()
chat_id, data = complete([{"role": "user", "content": "Hello GreenOps"}])
print(requests.get(f"{PROXY}/v1/chats", headers=headers, timeout=30).json())
print(requests.get(f"{PROXY}/v1/chats/{chat_id}", headers=headers, timeout=30).json())
requests.post(f"{PROXY}/v1/chats", headers=headers, json={"title": "New chat"}, timeout=30)
reply = data["choices"][0]["message"]["content"]
complete(
[
{"role": "user", "content": "Hello GreenOps"},
{"role": "assistant", "content": reply},
{"role": "user", "content": "Continue"},
],
chat_id,
)
requests.delete(f"{PROXY}/v1/chats/{chat_id}", headers=headers, timeout=30)
const PROXY = 'https://greenops-ai-proxy.copycomp-ai.workers.dev';
const TOKEN = 'GreenOpsKey123';
const headers = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${TOKEN}`,
};
async function complete(messages, chatId) {
const res = await fetch(`${PROXY}/v1/chat/completions`, {
method: 'POST',
headers: chatId ? { ...headers, 'X-Chat-Id': chatId } : headers,
body: JSON.stringify({ model: 'gemini-3.5-flash', messages }),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const data = await res.json();
return {
chatId: res.headers.get('X-GreenOps-Chat-Id'),
content: data.choices?.[0]?.message?.content,
};
}
const first = await complete([{ role: 'user', content: 'Hello GreenOps' }]);
const listed = await fetch(`${PROXY}/v1/chats`, { headers });
console.log(await listed.json()); // { chats: [{ id, title, preview, ... }] }
const detail = await fetch(`${PROXY}/v1/chats/${first.chatId}`, { headers });
console.log(await detail.json()); // { messages: [{ role, content, at }] }
await fetch(`${PROXY}/v1/chats`, {
method: 'POST',
headers,
body: JSON.stringify({ title: 'New chat' }),
});
await complete([
{ role: 'user', content: 'Hello GreenOps' },
{ role: 'assistant', content: first.content },
{ role: 'user', content: 'Continue' },
], first.chatId);
await fetch(`${PROXY}/v1/chats/${first.chatId}`, { method: 'DELETE', headers });
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
const string Proxy = "https://greenops-ai-proxy.copycomp-ai.workers.dev";
const string Token = "GreenOpsKey123";
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Token);
using var first = new HttpRequestMessage(HttpMethod.Post, Proxy + "/v1/chat/completions")
{
Content = new StringContent("""
{
"model": "gemini-3.5-flash",
"messages": [{ "role": "user", "content": "Hello GreenOps" }]
}
""", Encoding.UTF8, "application/json")
};
using var firstRes = await client.SendAsync(first);
firstRes.EnsureSuccessStatusCode();
firstRes.Headers.TryGetValues("X-GreenOps-Chat-Id", out var ids);
var chatId = ids != null ? string.Join("", ids) : "";
using var cont = new HttpRequestMessage(HttpMethod.Post, Proxy + "/v1/chat/completions");
cont.Headers.TryAddWithoutValidation("X-Chat-Id", chatId);
cont.Content = new StringContent("""
{
"model": "gemini-3.5-flash",
"messages": [
{ "role": "user", "content": "Hello GreenOps" },
{ "role": "user", "content": "Continue" }
]
}
""", Encoding.UTF8, "application/json");
using var contRes = await client.SendAsync(cont);
contRes.EnsureSuccessStatusCode();
Console.WriteLine(await client.GetStringAsync(Proxy + "/v1/chats"));
Console.WriteLine(await client.GetStringAsync(Proxy + "/v1/chats/" + chatId));
using var created = await client.PostAsync(
Proxy + "/v1/chats",
new StringContent("""{"title":"New chat"}""", Encoding.UTF8, "application/json"));
using var deleted = await client.DeleteAsync(Proxy + "/v1/chats/" + chatId);
Imports System.Collections.Generic
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports System.Text
Const Proxy As String = "https://greenops-ai-proxy.copycomp-ai.workers.dev"
Const Token As String = "GreenOpsKey123"
Using client As New HttpClient()
client.DefaultRequestHeaders.Authorization =
New AuthenticationHeaderValue("Bearer", Token)
Using first As New HttpRequestMessage(HttpMethod.Post, Proxy & "/v1/chat/completions")
first.Content = New StringContent(
"{""model"":""gemini-3.5-flash"",""messages"":[{""role"":""user"",""content"":""Hello GreenOps""}]}",
Encoding.UTF8, "application/json")
Dim firstRes = Await client.SendAsync(first)
firstRes.EnsureSuccessStatusCode()
Dim ids As IEnumerable(Of String) = Nothing
firstRes.Headers.TryGetValues("X-GreenOps-Chat-Id", ids)
Dim chatId = If(ids Is Nothing, "", String.Join("", ids))
Console.WriteLine(Await client.GetStringAsync(Proxy & "/v1/chats"))
Console.WriteLine(Await client.GetStringAsync(Proxy & "/v1/chats/" & chatId))
Using created = Await client.PostAsync(
Proxy & "/v1/chats",
New StringContent("{""title"":""New chat""}", Encoding.UTF8, "application/json"))
End Using
Using deleted = Await client.DeleteAsync(Proxy & "/v1/chats/" & chatId)
End Using
End Using
End Using
Department RBAC
Send X-User-Role on every Control Plane call. Roles isolate departments: FinOps never reads chat text; Compliance never leaves the audit trail.
| Role | Chat / history | Analytics | ESG export | Audit logs |
|---|
Cryptographic audit trail (EU AI Act)
GET /v1/audit/logs (Compliance / Admin) returns a private SHA-256 block-chain notary (schema v4) for every LLM transaction and IDP answer: each block signs timestamp, request ID, model, tokens, USD/CO₂, PII, risk, optional idp, blockHeight, chainId, prevHash and hash. Tampering breaks chainValid. Validate with GET/POST /v1/audit/verify (tamperEvidence). Headers: X-Audit-Hash, X-GreenOps-Notary. Built for EU AI Act logging duties in banking, insurance, and public sector.
ESG & FinOps monthly export
GET /v1/reports/monthly?month=YYYY-MM&format=json|csv|html with header X-Export-Reports (true/json/csv/html). Returns a monthly sustainability pack: economic savings (USD) + avoided carbon footprint (g/kg/t) for CSRD/ESG corporate filings. FinOps/Admin on Business or Enterprise receive X-Export-Reports: true and a Content-Disposition attachment. Free/Pro return 403 export_plan_required. Studio Dashboard exposes month picker + CSV/PDF/HTML download.
Stripe webhooks
Point the Stripe Dashboard endpoint at the URL below. The Worker verifies Stripe-Signature with stripe.webhooks.constructEvent when STRIPE_WEBHOOK_SECRET is set.
https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/webhooks/stripe
checkout.session.completedandinvoice.payment_succeededactivate Pro, Business or Enterprise (email +subscription_id+ plan → user record). New users get a welcome email with a password-setup link.- Plan is resolved from
metadata.plan, Payment Link, price nickname/amount ($29 / $149 / $499+), orSTRIPE_PRICE_*. customer.subscription.deleted,subscription.updatedwith status unpaid/canceled, or a finalinvoice.payment_faileddeactivates access (active: false, profile → Free).- Configure POST /v1/webhooks/stripe or POST /api/webhooks/stripe. Alias accepted. No browser CORS is required.
curl -X POST https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/webhooks/stripe \
-H "Content-Type: application/json" \
-H "Stripe-Signature: t=1700000000,v1=…" \
-d '{"type":"checkout.session.completed","data":{"object":{"customer_email":"dev@acme.com"}}}'
await fetch('https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/webhooks/stripe', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Stripe-Signature': 't=1700000000,v1=…',
},
body: JSON.stringify({
type: 'checkout.session.completed',
data: { object: { customer_email: 'dev@acme.com' } },
}),
});
import requests
requests.post(
"https://greenops-ai-proxy.copycomp-ai.workers.dev/v1/webhooks/stripe",
headers={
"Content-Type": "application/json",
"Stripe-Signature": "t=1700000000,v1=…",
},
json={
"type": "checkout.session.completed",
"data": {"object": {"customer_email": "dev@acme.com"}},
},
timeout=30,
)
Pricing tiers & quotas
Limits are enforced at the edge. Free is daily + burst; Pro and Business are monthly; Enterprise is unlimited.
| Plan | Price | Request limits | Features |
|---|---|---|---|
| Free | $0/mo | 1,000 req/day · 30 req/min | Advanced semantic cache, smart Flash routing, RAG compression, baseline PII, Community & Docs |
| Pro | $29/mo | 100,000 req/month · 1,000/min | OpenAI failover, 30-day cache TTL, real-time ESG/FinOps telemetry, automated PII anonymization |
| Business | $149/mo | 1,000,000 req/month · 5,000/min | ESG/FinOps PDF & CSV export, advanced PII rules, priority support |
| Enterprise | $499+/mo | Unlimited · 20,000/min | Dedicated Workers & storage, custom ESG audit, 99.9% uptime SLA |
Error handling & status codes
The gateway returns JSON errors. Chat quota responses include error.code, error.message and error.upgrade_url.
| Status | Name | Typical cause | What to do |
|---|---|---|---|
| 401 | Unauthorized | Missing/invalid Bearer or X-Proxy-Auth. | Send Authorization: Bearer GreenOpsKey123 (test) or your provisioned key. Check for extra spaces. |
| 429 | Rate Limit Exceeded | Free 1,000/day or 30/min; Pro 100k/mo; Business 1M/mo. Read X-RateLimit-Remaining. | Wait for Retry-After, or upgrade. Burst resets every 60 seconds. |
| 500 | Gateway Error | Unhandled Worker exception or upstream failure after retries. | Retry with backoff. Check GET /v1/health. Confirm Gemini/OpenAI keys if the model is cloud-hosted. |
| 404 | Not Found | Unknown /v1/chats/:id for this gateway key. | List chats with GET /v1/chats, then use an id from that payload. |
- 403: RBAC blocked the route (for example Audit requires Compliance/Admin) or a residual prompt attack was blocked after heuristic sanitization (
X-GreenOps-Sanitize: blocked). Corrected prompts continue withcorrected. - 400: malformed JSON, missing messages[], or a required provider key is absent for that model.
- SSE: send
stream: true. Cache HIT is returned as synthesized SSE (no paid upstream call). - 404 on /v1/chats/:id: the conversation is missing or belongs to another gateway key.
Testing & QA
GreenOps ships an automated suite so the gateway, semantic cache, chat history, and the Studio docs modal stay copy-ready. Backend tests run in Vitest against an in-memory mock Worker (or a live origin when GREENOPS_URL is set). Frontend tests run in Playwright against the static Studio server on port 4173.
- Install once:
npx playwright install chromium(needed for E2E). Vitest is a local npm dependency. - Default API tests never call production:
tests/helpers/mock-greenops-worker.jsmocks completions, KV cache HIT/MISS, and/v1/chats. - Point API tests at a live Worker with
GREENOPS_URLand optionalGREENOPS_AUTH.
Backend · Vitest
tests/api.spec.js covers the OpenAI-compatible gateway: missing Bearer returns 401; a valid key returns 200 with FinOps/ESG headers; an identical prompt is a semantic-cache MISS then HIT; GET/POST/DELETE /v1/chats persist and drop history. Run with npm run test:api (vitest run tests/api.spec.js).
Frontend · Playwright
tests/e2e/docs.spec.js opens Studio, clicks Documentation, fetches same-origin /docs.html without redirect loops, and checks the copyable language tabs (cURL, Python, JavaScript/TypeScript, C#, VB.Net) in English and Italian. Playwright starts node scripts/studio-static.mjs on port 4173. Run with npm run test:e2e.
Quick commands
From the repository root. npm test runs API tests first, then E2E.
| Command | What it runs |
|---|---|
npm test | Runs all tests |
npm run test:api | Runs the API tests |
npm run test:e2e | Runs the E2E tests |
npm test
npm run test:api
npm run test:e2e
Contact & support
Reach GreenOps Studio by email. Technical issues go to support; general inquiries to info; Enterprise and commercial requests to sales.
- Technical support: support@greenopsstudio.com
- General info: info@greenopsstudio.com
- Sales / Enterprise: sales@greenopsstudio.com
- Terms of Service · Refund Policy (COPYCOMP SRLS)