Documentation
Members & roles
How INFRO organizations work: one bill and shared projects, the four member roles and their exact permissions, invites, role changes, and removal.
An INFRO organization is the boundary your team shares: one bill and credit balance, one set of projects and API keys, and one set of policies — spend controls, model allowlists, and the EU routing zone if you pin one. Everyone you add is a member with exactly one of four roles: owner, admin, developer, or analyst.
Manage membership in the console under Organization → Members, or with the API below. Every change on this page — invite, role change, removal — lands in the audit log.
What an organization shares
- One bill. All usage across every project and key draws from the same credit balance and appears on one invoice. Per-key and per-member attribution comes from analytics.
- Shared projects and keys. Projects group keys and carry their own policies, like a model allowlist. Keys are created by members but belong to the organization — anyone with the right role can see and revoke them.
- Shared policies. Spend limits can be set at the organization, project, member, or key level; alerts and compliance settings apply org-wide.
Org endpoints use the same Authorization: Bearer header as everything else — see authentication. A key acts with the role of the member who created it: a key created by a developer can read the member list but not change it. A request beyond the key's role returns 403 with type permission_denied, using the standard error envelope.
Roles
The four roles are fixed — there are no custom roles. Pick the narrowest role that covers what the person does; you can change it later with a single PATCH.
| Role | Manage billing | Manage members | Manage policies | Create keys | Call the API | Read analytics |
|---|---|---|---|---|---|---|
owner | Yes | Yes | Yes | Yes | Yes | Yes |
admin | — | Yes | Yes | Yes | Yes | Yes |
developer | — | — | — | Yes | Yes | Yes |
analyst | — | — | — | — | — | Yes |
ownerrole- Everything, including billing, plan changes, and deleting the organization. Only an owner can grant or remove the
ownerrole, and the last owner cannot be removed or demoted. When SSO is enforced, keep at least one password+MFA owner as break-glass access. adminrole- Everything except billing: invites and removals, role changes, spend controls — though the org-wide limit itself is owner-only — model allowlists, key management, and the audit log — which only owners and admins can read.
developerrole- Day-to-day building: creates keys, calls the API, reads analytics and request logs. Cannot change membership, policies, or billing.
analystrole- Read-only: console dashboards, usage, and exports. Analysts cannot create keys or call model endpoints — the right role for finance and data teams who need numbers, not access.
Invite a member
POST /v1/org/invites sends an email invite; the recipient joins with the role you set when they accept. Owners and admins can invite, but only an owner can invite another owner. The console equivalent is Organization → Members → Invite.
curl https://api.infro.io/v1/org/invites \
-H "Authorization: Bearer $INFRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "dana@acme.dev",
"role": "developer"
}'emailstringrequired- Address the invite is sent to. If the person already has an INFRO account under this address, accepting adds the organization to it; otherwise they create one on accept.
rolestringrequired- One of
owner,admin,developer,analyst. Any other value returns400 invalid_request_error.
{
"id": "inv_4t8s2c",
"email": "dana@acme.dev",
"role": "developer",
"status": "pending",
"expires_at": "2026-09-07T09:12:00Z"
}Invites expire after 14 days. Re-posting the same address reissues the invite and resets the expiry; pending invites can be revoked in the console. Each accepted invite writes a member.invited entry to the audit log.
Read the organization and its members
GET /v1/org returns the organization itself; GET /v1/org/members lists members with cursor pagination — limit up to 100 (default 25), and a cursor taken from the previous page's next_cursor.
curl https://api.infro.io/v1/org \
-H "Authorization: Bearer $INFRO_API_KEY"
curl "https://api.infro.io/v1/org/members?limit=25" \
-H "Authorization: Bearer $INFRO_API_KEY"{
"data": [
{
"id": "mem_2c81aa",
"email": "a.keller@acme.dev",
"role": "owner",
"spend_limit": null,
"source": "invite",
"joined_at": "2025-11-04T09:12:00Z"
},
{
"id": "mem_9d2f81",
"email": "dana@acme.dev",
"role": "developer",
"spend_limit": { "monthly": 500.00, "behavior": "block", "alert_at": [0.5, 0.8, 1.0] },
"source": "scim",
"joined_at": "2026-08-24T10:03:00Z"
}
],
"has_more": false,
"next_cursor": null
}idstring- Member ID, used in
PATCHandDELETEcalls below and as thekeyscope actor in the audit log. rolestring- One of
owner,admin,developer,analyst. spend_limitobject | null- Per-member spend limit,
nullif none. The shape and reset behavior are defined in spend controls. sourcestringinvitefor members added by email invite,scimfor members provisioned by your identity provider — see SSO & SCIM.joined_atstring- UTC timestamp of when the member accepted the invite or was provisioned.
Change a role or set a member spend limit
PATCH /v1/org/members/{id} updates role, spend_limit, or both, and returns the updated member. Role changes take effect immediately — the member's console session and existing keys pick up the new permissions on their next request. Only an owner can promote to or demote from owner.
curl -X PATCH https://api.infro.io/v1/org/members/mem_9d2f81 \
-H "Authorization: Bearer $INFRO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"role": "admin",
"spend_limit": { "monthly": 1000, "behavior": "block", "alert_at": [0.5, 0.8, 1.0] }
}'A member spend limit caps what all of that member's keys spend combined; with "behavior": "block", requests over the limit fail with 402 insufficient_credits, the same status a per-key limit produces — see spend controls for the full object, alert_at thresholds, and reset timing.
Demoting a member to analyst revokes every key they created, since analysts cannot hold keys. The revocations appear as key.revoked entries in the audit log alongside the member.role_changed entry.
Remove a member
DELETE /v1/org/members/{id} returns 204 and takes effect immediately: the member's console sessions end, and every API key they created is revoked at the same moment — subsequent requests with those keys return 401 authentication_error. Their historical usage, request logs, and audit entries are retained and stay attributed to them.
curl -X DELETE https://api.infro.io/v1/org/members/mem_9d2f81 \
-H "Authorization: Bearer $INFRO_API_KEY"If production traffic runs on a key created by someone who is leaving, rotate it to a key created by another member before removing them — removal revokes their keys with no grace period. Rotation is zero-downtime; the steps are in authentication.
Everything is audited
Invites, role changes, and removals write member.invited, member.role_changed, and member.removed events to the audit log, each with the acting member, the target, and a UTC timestamp. The log is readable by owners and admins, retained for 12 months, and exportable via exports.
Let SCIM own membership instead
With SCIM 2.0 provisioning enabled, your identity provider becomes the source of truth: joiners, leavers, and IdP-group-to-role mapping all flow from the IdP, and provisioned members show "source": "scim". Manual invites via POST /v1/org/invites still work — useful for a contractor who isn't in your directory — and a manual role PATCH on a SCIM-managed member succeeds, but it is overwritten on the next sync from your IdP's group mapping, so make lasting role changes for those members in the IdP. Deprovisioning removes the member and revokes their keys exactly like a DELETE. Setup lives in SSO & SCIM; the wider enterprise picture is on /enterprise.