Your clients' API keys shouldn't live in your .env
If client API keys live in your .env and their automations live on your laptop, every handover is a mess. Give each client its own Minion organization instead: keys they own, roles for your team, and a handover that's mostly removing people.
A few months into building automations for other people, open your .env file and count. Four clients, say, and eleven API keys. Two belong to a company you stopped working with in the spring, and one is a HubSpot token nobody remembers creating.
If you run an AI automation agency, or you're a consultant who builds agent workflows on the side, you probably know this file. The client's keys live on your laptop. The scripts live in a folder named after the client, or worse, in a conversation with your own agent. It works fine right up until the engagement ends and someone asks, "So how do we keep this running?"
That question is why Minion has organizations, and why I built them to work for agencies as much as for internal teams.
Where an AI automation agency's work actually lives
Be honest about where the pieces are on a typical client project:
- The keys are in your
.env, your password manager, or pasted into a chat at some point. - The code is a script your agent wrote, sitting on your machine or buried in a chat transcript.
- The schedule is a cron job on your box, or a reminder to run it manually on Mondays.
- The context (why the CRM sync skips archived deals, which inbox the client actually reads) is in your head.
None of that belongs to the client. It belongs to you, and to whichever agent you happened to use that week. So when you leave, either you keep running their automations forever as a favour, or you write a handover doc and hope.
I don't think the fix is a better handover doc. The fix is building the work in a place the client can own from day one.
One organization per client
In Minion, every client gets their own organization. Each one has its own packages, secrets, connections, inbox address, memories and runs. Nothing crosses between organizations. A key stored for one client is never visible to another, and a package you saved for client A doesn't show up when you're working for client B.
You don't need a separate login for each. One account belongs to every organization you're in, and switching clients is one click. Your agent works in whichever organization you have selected, so the HubSpot sync you're building for one client can't accidentally pick up another client's token.
If you want your agent to confirm where it is before it does anything, there's a call for that:
const { organization, role } = await minion.account.me()
return `${organization.name} (${role})` I like having this at the top of anything that writes to a client system. It's cheap, and it turns "which client am I in?" from a guess into a fact.
How to manage client API keys without holding them
This was the part I cared about most, because client API keys are the thing agencies get wrong most often. Not out of carelessness. There just isn't a good default place to put them.
In Minion, a secret is stored on the client's organization and used by name. Your agent writes code that refers to it, and Minion fills in the value on the outbound request:
const res = await fetch('https://api.hubapi.com/crm/v3/objects/deals?limit=50', {
headers: { Authorization: 'Bearer {{secret:hubspotToken}}' },
})
return (await res.json()).results.length No capability returns a secret's value. Not to you, not to your agent, not to the model provider's logs. The {{secret:name}} placeholder only resolves for hosts an owner or admin has approved, so even if a script tried to send the token somewhere else, it wouldn't go.
That changes who holds what. The client (or you, acting as an owner while you set things up) stores the key and approves which hosts it may be sent to. Your team members can run the automations that use it without ever seeing it. When the client rotates it, nothing in your code changes, because the code never had the value in the first place. There's more on how this works on the secrets page and in the secrets docs .
Roles for your team and the client's
Agency work usually means a mix of people: your builders, maybe a project lead, and someone on the client side who needs to approve things. Minion has two levels inside each organization.
Members can:
- run tools and jobs
- use stored keys by name, without seeing the values
- save new versions of packages
- use the organization's connections and integrations
Owners and admins can also:
- create and rotate keys, and approve which hosts each key may reach
- publish and lock packages
- add and approve connections
- invite people and manage billing
A pattern that works well: make the client an owner from the start, and keep your builders as members. Your team can do all the day-to-day work. The client stays in charge of their own credentials and who has access. If you're doing the setup yourself, you can be an owner or admin too while you build.
Every organization also keeps an activity log of sign-ins, invitations, key changes and publishes, and every run keeps its logs and result. When a client asks who changed something, you can show them instead of reconstructing it from memory.
A clean client handover for AI automations
Here's the part that usually hurts. In Minion, ending an engagement is mostly a matter of removing people.
When you remove a teammate from an organization, their access to that organization ends. The packages, jobs and notes they built stay with the client. That's deliberate. The work was done for the client, so it lives with the client.
So a handover looks like this:
- The client is already an owner, so they already hold their keys and connections.
- Your scheduled jobs keep running, because they run in Minion, not on your laptop.
- You leave a handoff note in their organization explaining what each package does and what to watch.
- You remove your team. Or you stay on as members for ongoing support, if that's the deal.
That handoff note is a regular Minion call, and whichever agent the client's people use next will find it:
await minion.notes.handoff({
title: 'CRM sync: handover',
body: '- Runs nightly at 02:00\n- Skips archived deals on purpose\n- If it fails, check the hubspotToken expiry first',
}) And if the client ever wants to leave Minion entirely, owners can export everything their organization holds as JSON, or delete the organization outright. Secret values are never included in an export, which is what you want: the client already has their own keys.
Billing is per organization too. Each one has its own plan and usage, managed by its owners and admins, so one client's usage never lands on another client's bill. Plans are on the pricing page .
What this doesn't do
Minion doesn't make the automations for you, and it makes no inference calls of its own. Your agent (Claude, Claude Code, Cursor, Codex or ChatGPT, over MCP) does the thinking. Minion is where the code, keys, data and schedules end up, so the work outlives the chat. I wrote more about that split in the assistant that can't leak your keychain .
It also won't fix a client who never rotates their keys. But it does make sure that when they do, you're not the one holding the old ones.
Try it
Create an organization for one client you're working with now. Move one key out of your .env into it through the dashboard (never by pasting it into chat), approve its host, and have your agent rewrite one script to use {{secret:name}} instead. The first steps guide walks through connecting your agent, and the business page covers how organizations work for agencies. You can sign up here .