Skip to content
← BlogDeveloper

Managing multiple Norbelys orgs from one terminal with the CLI

How the norbelys CLI's one-profile-per-org config file works, and the safe patterns for switching between client workspaces without hitting the wrong org.

By Gabriel Lara, Developer Relations, Norbelys

Founder-reviewed ·How we research and correct articles

An agency running Norbelys for a dozen clients isn’t managing one workspace — it’s managing a dozen, each its own Clerk organization with its own senders, its own audience, its own suppression list. Clicking into the dashboard and switching orgs from a menu works fine for a person doing one thing at a time. It works less well the moment you want to run the same check, the same import, or the same health audit across every client workspace without doing it by hand twelve times.

The CLI’s config file is built around exactly this: one profile per org, in a single file, on one machine. This post covers how that actually works and — more importantly — how to avoid the one mistake it makes possible: running a command against the wrong org.

What’s actually in the config file

Every norbelys login writes its result to ~/.config/norbelys/config.json, and the file holds one profile per organization you’ve authenticated against. A separate post covers why that file is locked down to mode 0600 — worth reading first if you haven’t, since everything below assumes that baseline: the file is only readable by you, and it’s the thing standing between “a script mistargets a client” and “a script mistargets a client and the credential that made it possible was sitting around loosely permissioned too.”

It’s a plain JSON file, which is worth remembering in its own right — if you’re ever unsure what’s configured, you can open it and read it directly instead of trusting what you think you remember setting up three months ago.

Adding a second org safely

Bringing a new client workspace under the CLI

  1. Generate a dedicated API key for that org

    From that client's own dashboard, not a key copied from a different workspace. A key is org-scoped by design — using the right org's key from the start removes an entire category of mistake before it can happen.

  2. Log in with that key

    norbelys login --api-key ak_... against the new key. This adds or updates that org's profile in config.json without touching any other org's saved credential.

  3. Confirm which org you're actually pointed at

    Run norbelys whoami immediately after. Don't assume the login succeeded against the org you meant — verify it, especially the first time you're setting up a new client.

  4. Name things so future-you doesn't have to guess

    If you're scripting around multiple orgs, keep a clear mapping somewhere outside the CLI itself — a README, a secrets manager entry — between client name and which key belongs to which. The config file identifies orgs; it doesn't remind you which client that org is.

Terminal
$ norbelys login --api-key ak_client_a_...Logged in to Acme Inc (org_8h2k...)$ norbelys whoamiAcme Inc org_8h2k... ak_...a91f

The real risk: running against the wrong org by habit

The failure mode here isn’t exotic — it’s the same one that bites anyone juggling multiple AWS accounts or multiple kubectl contexts. You run a command you’ve run a hundred times, on muscle memory, and it executes against whichever org happened to be active last, not the one you meant this time. For a read like people list, that’s an annoyance. For a write — deleting a suppressed contact, launching a program — it’s a real incident, and it’s one client’s data affected by a mistake meant for another.

The pattern that actually removes the risk: be explicit in scripts

Interactive habit-checking helps, but it’s still a human remembering to do something. For anything scripted, there’s a more reliable pattern already built into the CLI: the bearer token resolves from an environment variable before it falls back to a saved profile. That means a script touching multiple orgs doesn’t need to rely on “whichever profile is currently active” at all — it can set NORBELYS_API_KEY explicitly for each org, for each run, so there’s no ambient state to get wrong in the first place.

A loop across client orgs looks roughly like this — each iteration overrides the environment variable for that one command, rather than depending on a persisted “current” profile:

agency-check.sh
# keys pulled from a secrets manager, not hardcoded$ for client in acme northwind globex; do$ key=$(get_secret "norbelys/${client}")$ echo "== ${client} =="$ NORBELYS_API_KEY="$key" norbelys senders list --json | jq '.[].status'$ done

That NORBELYS_API_KEY="$key" norbelys ... prefix — setting the variable for a single command rather than exporting it for the whole shell session — is the important part. It scopes the credential to exactly one invocation, so there’s no window where a leftover exported variable from checking Acme’s senders accidentally applies to a command you meant to run against Northwind two lines later.

Separating interactive use from scripted use

A practical split that holds up in practice: use norbelys login and the saved profile for whichever org you’re interactively poking around in right now — one active context at a time, checked with whoami before anything destructive. Reserve the environment-variable override for anything scripted or repeated across orgs, where you want the credential explicit at the point of use instead of implicit in whatever you logged into last. Mixing the two — writing a multi-org script that quietly depends on the profile you happen to have active — is exactly the setup that produces the wrong-org mistake eventually, even if it works fine the first several times you run it.

Naming senders and resources so cross-org output stays readable

One thing that trips people up the first time they run a check across several orgs: the output of most commands only tells you about the resource inside its own org, not which org it came from — a senders list result doesn’t stamp “this is Acme’s data” on every row, because from the API’s point of view there’s no such thing as a cross-org list to begin with. When you’re piping several orgs’ output into one place, as in the loop above, label it yourself — the echo "== ${client} ==" line before each call isn’t decoration, it’s the only thing that keeps twelve orgs’ worth of JSON from reading as one undifferentiated blob once it’s scrolled past.

If the config file gets lost or a key gets rotated

Losing config.json isn’t a data-loss event — it holds credentials, not anything Norbelys itself considers a source of truth. Re-run norbelys login per org and you’re back where you started. The direction that actually matters is the other one: if a key is compromised or a client relationship ends, revoke that org’s key from its dashboard and run norbelys logout (or just delete the stale entry) so an old credential isn’t sitting in a config file for a workspace you no longer manage.

Next steps