How SaltingIO team roles and Team IDs let engineers use shared credentials at request time without ever seeing the raw secret.

A small team ships a frontend that talks to OpenAI, Stripe, and an internal metrics API. Every engineer needs those keys to build and test, so the keys drift into a shared password entry, a pinned Slack message, or a .env file that ends up committed to a branch nobody remembers to delete. Now a contractor rotates off the project and the only safe move is to rotate three upstream keys and redeploy everything that used them.
The cleaner model is to stop distributing the secret at all. With SaltingIO, a credential or a Bridge lives encrypted at rest and is only ever resolved at request time behind a single endpoint. Team members call the endpoint; they never hold the upstream key. Revoking someone means removing them from the team, not rotating the world.
This walks through how records, roles, and Team IDs fit together, and how a teammate actually calls a team-scoped record. One thing up front: team access control is a plan-gated capability on SaltingIO. It sits on the Enterprise Edge tier, so if you are on Starter or Builder and the Teams options look greyed out, that is expected and not a bug. Check https://salting.io/pricing before you build a workflow around it.
A .env file distributes the raw secret to every machine that clones the repo. That is the core problem. Once the string sk_live_... is on a laptop, it is in shell history, in editor swap files, in the backup that laptop syncs to iCloud, and in any log line that accidentally prints the environment. You cannot audit who read it, and you cannot revoke one person without invalidating it for everyone.
Scoping the secret to a SaltingIO record inverts that. The upstream key is stored once, encrypted with AES-256-GCM, and the thing you hand to a teammate is an endpoint plus a per-record access key. The upstream credential itself is never returned to the caller. For a Bridge, the request is forwarded to the destination and only the upstream response comes back, so the developer wiring up the call never sees the OpenAI or Stripe key at all.
SaltingIO teams have three roles, and the copy in the role picker is worth quoting exactly so nobody is surprised by what they can do:
The team Owner cannot be demoted, which matters when you are deciding who creates the team in the first place. Make it an account you control long term, not a contractor's login.
Invites do not go out by email. You copy a Team ID from the dashboard and share it with the person you want to add, and they join with it. That is a deliberate design choice, and it has a consequence: a Team ID is a bearer credential. Anyone who has it can join. Treat it like a secret, send it out of band, and regenerate it if it leaks.
Records can be scoped to a team, which is the piece that ties this together. A Bridge that proxies OpenAI can belong to the team, and every Editor can update its destination or headers while Viewers can only read its configuration. The upstream key inside it stays invisible to all of them.
Once a record is created as Private, its access key is shown exactly once, on the success modal at creation time, next to a Download .txt button. It is never shown again. Copy it immediately, because the only recovery path is rotating the record. This trips up almost everyone the first time.
A private record is called with the X-API-Key header. Do not reach for Authorization: Bearer here; Bearer tokens are dashboard JWTs and are not valid on /r/ endpoints.
curl -H "X-API-Key: your-record-key" \
https://api.salting.io/r/9f3c1a72-4d5e-4b0a-9c11-8e2f6a0b3d17
For a Bridge that fronts OpenAI, the same call forwards to the upstream and returns the upstream JSON. You can trim that response before it reaches the browser with a select transformation, so a chat completion comes back as just the text your UI needs:
curl -H "X-API-Key: your-record-key" \
"https://api.salting.io/r/9f3c1a72-4d5e-4b0a-9c11-8e2f6a0b3d17?select=choices.0.message.content"
Template variables let one team-scoped Bridge serve many routes. If the stored destination is https://api.openai.com/v1/{{path}}, the caller supplies ?path= at request time, and the single record covers several upstream endpoints without leaking the key on any of them.
When you want to check behavior without writing a client, the in-dashboard Playground runs single or batch calls with param injection, custom headers, and a transformation preview. It also has a generator that produces a paste-ready prompt for AI tools, embedding the endpoint URL and a real response body, which is a fast way to hand a new team member exactly the context they need to wire up the call in Cursor or Claude.
The access key shown once is the first thing that bites people. If you close the creation modal without copying it, there is no "show key again" button. You rotate the record and update every caller. Build the habit of hitting Download .txt on the success screen every single time.
The second is a role expectation mismatch. A Viewer who tries to update a record will not get a helpful "you lack permission" popup in every flow; they simply cannot save. If a teammate swears their edit is not sticking, check their role before you check your code. Editors can create, read, and update, but team management stays with Admins, so promoting someone is itself an Admin action.
The third is confusing a 401 for a 404. Calling a private record without the header returns {"error":"Authentication required","message":"Please provide your token in the X-API-Key header to decrypt the content."}. Calling a record that does not exist, or one your team no longer has, returns {"error":"Can not find the stored information."}. If a teammate who was just removed from the team reports the second error, that is the access change working, not a broken UUID. And if you get {"error":"Origin not allowed","message":"Your origin is not authorized to access this endpoint."} from the browser but the same call works from curl, the record has an origin allowlist that does not include your dev host.
Sharing credentials across a team does not have to mean copying strings between machines. Store the secret once, scope the record to the team, and let roles decide who can change it and who can only read it. Nobody on the team ever holds the raw upstream key, and offboarding is a membership change rather than a rotation drill. Just confirm the tier you need on the pricing page first, since team access control lives on Enterprise Edge.
Read the docs to see the exact role picker and Team ID flow before you invite anyone.
Professional API security without the "Backend Tax."