{"slug": "stop-sharing-your-openai-api-key-with-your-team", "title": "Stop sharing your OpenAI API key with your team", "summary": "A developer built Melony, a hosted team chat product for agents built on the OpenAI Agents API or Claude Managed Agents, after concluding that sharing a single API key across a team grants account-wide access rather than access to one agent. The architecture routes all browser traffic through a server-side proxy that enforces membership and daily limits, stores the platform key server-side, and keeps a lightweight session access index so users see only their own and shared sessions, returning 404 rather than 403 to prevent probing. The developer also recommends denying tool calls awaiting approval instead of building an approval UI, and warns that teams should weigh the ongoing maintenance of such a layer against simply sharing a key.", "body_md": "You built an agent on the OpenAI Agents API, or on Claude Managed Agents. It works. You show it to the support lead and they say: *\"Can my team use this tomorrow?\"*\n\nThe fastest answer is to paste the API key into Slack along with a script, and let everyone run it.\n\nPlease don't. Here's why, and what I learned building the alternative.\n\nA shared API key isn't access to *one agent*. It's access to your account.\n\n`.env` files, screenshots and someone's personal laptop. Each copy is a liability.\nWhat you actually want is to share **the agent**, not **the key**.\n\nWhen I started, I thought it was a text box and a streaming response. It turned out to be eight things:\n\nNone of these is hard on its own. Together they're a product.\n\nThe heart of it is a small proxy. The browser talks only to your API. Your API checks who's calling, then calls the platform with the key:\n\n```\n// Pseudocode: the shape of every chat request\napp.post(\"/v1/chat\", requireSignedInUser, async (req, res) => {\n  const agent = await db.getAgent(req.body.agentId);\n\n  // 1. Membership: return 404, not 403, so non-members can't probe which agents exist\n  if (!(await db.isMember(agent.id, req.user.id))) return res.sendStatus(404);\n\n  // 2. Limits, enforced here, not in the UI\n  if (await overDailyLimit(req.user.id)) return res.sendStatus(429);\n\n  // 3. The key comes from server-side storage and never leaves this process\n  const apiKey = await secrets.get(agent.id);\n\n  // 4. Call the platform and stream a normalized event format back to the browser\n  const events = platformFor(agent).send({ apiKey, agent, sessionId: req.body.sessionId, message: req.body.message });\n  streamAsSSE(events, res);\n});\n```\n\nA few rules I'd now treat as non-negotiable:\n\nThis one took me a while to notice. Hosted agent platforms run sessions for **your API key**, not for your users. They don't track which person started which session.\n\nSo if you want \"my chats are private\", you need your own small **access index**:\n\n```\n// One row per session. This is not the conversation; the messages stay on the platform.\ntype SessionAccess = {\n  sessionId: string; // the platform's session id\n  ownerId: string;   // your user id\n  shared: boolean;   // visible to everyone on the agent?\n  createdAt: Date;\n};\n```\n\nThe sidebar lists sessions you own plus the shared ones. Any request for a session you can't see returns 404 **before** calling the platform. That's also why I don't store messages at all: the platform already does, and keeping a second copy is just more data to protect.\n\nBoth platforms support tools, and some setups pause a tool call until someone approves it. If your chat UI has no approval flow, that session just waits forever.\n\nMy fix: don't build an approval UI. Deny calls that are waiting for approval, with a clear message pointing at the agent's permission policy. Configure permissions where the agent lives, on the platform, and keep the chat UI simple.\n\nIf the chat is part of a **customer-facing product**, build it. You'll want your own auth, branding and billing anyway.\n\nIf the users are **your own team**, think twice. Everything above is maintenance you'll carry as both platforms change.\n\n*Disclosure: I went through all of this and turned it into [Melony](https://melony.dev), a hosted team chat for agents built on the OpenAI Agents API or Claude Managed Agents. You connect the agent with its key once, invite teammates by email, and the key stays on the server. It's free, and the [security page](https://melony.dev/security/) lists exactly what it stores. But the pattern above works whether you use it or build your own.*\n\n**How are you sharing agents with your team right now?** A shared key, an internal tool, or something else? I'd like to hear what's working for you.", "url": "https://wpnews.pro/news/stop-sharing-your-openai-api-key-with-your-team", "canonical_source": "https://dev.to/ddaras/stop-sharing-your-openai-api-key-with-your-team-1fek", "published_at": "2026-10-19 19:30:47+00:00", "updated_at": "2026-10-19 19:44:32.771225+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "ai-products", "developer-tools"], "entities": ["Melony", "OpenAI Agents API", "Claude Managed Agents", "OpenAI", "Anthropic"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/stop-sharing-your-openai-api-key-with-your-team", "markdown": "https://wpnews.pro/news/stop-sharing-your-openai-api-key-with-your-team.md", "text": "https://wpnews.pro/news/stop-sharing-your-openai-api-key-with-your-team.txt", "jsonld": "https://wpnews.pro/news/stop-sharing-your-openai-api-key-with-your-team.jsonld"}}