Key concepts
Principal
Whoever or whatever is being granted access or checked for permissions — a person (user:<user-id>) or a service account (service_account:<id>, see Service accounts / API keys below). Referenced the same way whether you’re assigning a role, revoking one, or checking a grant.
Role
A named bundle of permissions. Today every role is a built-in role (e.g. roles/owner, roles/viewer) — you can’t yet define your own custom role, only assign or revoke the built-in ones.
Role assignment (grant)
A principal holding a role, either org-wide or narrowed to a single project. Both directions are idempotent: revoking a role a principal doesn’t hold at that scope is a safe no-op, and assigning the same role to the same principal at the same scope twice does not create a duplicate grant — it’s enforced as a no-op at the database level, not just by well-behaved callers.
Permission
A single, fine-grained capability (e.g. network.vpc.list), identified by its key. Roles are made of permissions; you can check whether a specific principal has a specific permission on a specific resource without needing to know which role would grant it.
Assigning and revoking roles
Assign a built-in role to a user, either across their whole organisation or narrowed to one project — omit the project to grant it org-wide. Revoking works the same way and must match how the role was originally assigned: an org-wide grant needs an org-wide revoke, a project-scoped grant needs the same project specified.
assign-role/revoke-role need the target’s raw user:<user-id> — you have to already know it. For inviting a real person by an identifier they’d actually have, see Managing org members below.
Managing org members
Invite a person to your org by email, list who currently has access, and remove someone’s access — org-wide only, not narrowed to a single project.
- Invite: if the email already has an account, they get org access immediately — no separate “accept” step. If it doesn’t have an account yet, the invite is stored and applied automatically the moment that email signs up and authenticates for the first time. Either way, the invitee gets a real notification email (see #296) — delivery is best-effort and never blocks the invite/grant itself, so a bounced or delayed email doesn’t affect access.
- List members: every principal with an active role assignment on the org, together with their role(s) and (where resolvable) their name/email.
- List invites: pending invites that haven’t been applied yet — someone invited by an email with no account.
- Remove a member: revokes every role they hold in the org, at every project scope, not just one specific grant — use
revoke-roleinstead if you only want to pull back a single role. - Cancel an invite: revokes a pending invite before it’s ever applied.
Requires roles/owner (or an equivalent custom grant) — roles/viewer can list members and invites but can’t invite or remove anyone.
Service accounts / API keys
A service account is a non-interactive credential for automation — CI pipelines, the Terraform provider, scripts — scoped to an org and authenticating as its own principal (service_account:<id>), checked against has_permission exactly like a user.
- Create: returns a real OAuth
client_id/client_secretpair (aclient_credentialsgrant against the platform’s identity provider). Theclient_secretis shown exactly once, at creation — store it immediately, it can’t be retrieved again afterward. A newly created service account holds no roles: grant it access withassign-roleagainst its principal, the same as you would for a user. - List: every service account created for the org, active or revoked, without ever exposing a secret.
- Revoke: immediately invalidates the credential — the next request made with it is rejected. Existing role assignments are left in place; revoke those separately with
revoke-roleif you also want to pull back access before deleting the credential.
Requires roles/owner (or an equivalent custom grant) to create or revoke; roles/viewer can list.
Audit log
A durable, append-only record of administrative/IAM actions taken in your org — who did what, and when. Today it only covers service account creation and revocation; other actions (role assignment/revocation, org/project lifecycle, and denied authorization attempts) aren’t recorded yet. Each entry has the actor, a dotted action key (e.g. iam.service_account.create), the affected resource, when it happened, and action-specific details.
Requires roles/owner or roles/viewer (or an equivalent custom grant) to read; there’s no way to delete or modify an entry through the API.
Reading it is paginated, and the response tells you whether you have read all of it. auditLogEntries returns a page, not a bare list:
entries— this page, newest first. You can narrow to one project, or read the whole org.total— how many entries match your query in total, across every page.hasMore— whether anything remains beyond this page. When it’sfalse, you have the complete trail for that query.offset/limit— the window you were actually served. Ask for the next page by addinglimittooffset, and keep going whilehasMoreis true.
limit defaults to 100 and is capped at 500 per page; ask for more and you’ll be served 500, with the limit in the response saying so. The cap bounds one response, not how far back you can read — every entry is reachable by paging, however long the trail is. Entries are ordered by when they happened, newest first, with a stable tiebreaker, so paging never skips or repeats an entry.
One caveat inherent to reading a log that is still being written: if new entries are recorded while you page through, they land at the front and shift everything after them, so an entry can show up on two consecutive pages. Nothing is dropped — you may see something twice, never miss it. If you need an exact snapshot, de-duplicate on the entry id.
Checking access
You can look up your own current grants, another principal’s grants, or check directly whether a principal has a specific permission on a specific resource — useful for building your own automation that needs to know in advance whether an action will succeed, rather than just attempting it and handling a permission error.
Organisation status
A new organisation is either usable immediately or placed under review, depending on how much spare capacity the platform has at the moment you create it. You don’t choose this, and there’s nothing to opt into — most organisations are activated on the spot.
An organisation under review can still be administered normally: invite and remove members, assign and revoke roles, create service accounts, set up projects and billing. What it can’t do yet is create billable infrastructure — launching an instance, creating a volume, creating a VPC, or allocating a floating IP all fail with an explicit “not active” error rather than silently doing nothing.
The decision is the platform operator’s to make; there’s no customer-facing way to approve your own organisation or to ask for a decision through the API. You’ll get an email when your organisation is created, and another when it’s approved or rejected. Rejection is final — there’s no path back from it.
You can read the current status directly: it’s the status field on the organisation (PENDING_REVIEW, ACTIVE, or REJECTED), returned by the org queries and shown by origin admin get-organisation and list-organisations.
Billing access
Billing is deliberately not part of roles/owner. Being an Owner lets you switch which billing account one of your projects is charged to, but not view, create, or change the billing account itself — that needs one of three dedicated roles, assigned org-wide:
roles/billing/administrator— full access: view, create, update, delete, and manage linked projects.roles/billing/user— everything except deleting a billing account.roles/billing/viewer— read-only.
Whoever creates an organisation is granted roles/billing/administrator on it automatically, alongside roles/owner, so the person who signs up can manage billing from the start. Nobody else gets it implicitly: a member you invite later holds only the roles you assign them, so an Owner you add has no billing access until you grant it explicitly.
These roles are org-wide only — assign them with the project omitted. Narrowing one to a single project is untested and unsupported; nothing rejects the call, but the grant will not behave the way you expect.
What’s not available yet
- Groups. Groups exist as a concept in the API but are currently name-only placeholders — not yet a real principal you can assign roles to or check permissions against. Don’t build automation around group-scoped access yet.
- Custom roles. You can only assign or revoke the platform’s built-in roles today; defining your own role with a custom permission set isn’t supported yet.
- Conditional role assignments. A role assignment is unconditional today — there’s no way to scope a grant to, for example, a time window or an additional attribute check.
- Per-key permission scoping. A service account’s access is exactly the union of roles assigned to it — there’s no way to narrow an individual credential to a subset of what its roles would otherwise allow.
- External IdP federation. Service accounts authenticate against the platform’s own identity provider only; there’s no OAuth client-credentials federation with an external identity provider.
- Full audit coverage. The audit log only records service account creation and revocation so far — role assignment/revocation, org/project lifecycle changes, member invites/removals, and denied authorization attempts aren’t recorded yet.
Using IAM
- The GraphQL API — the primary interface. Look under the
iamnamespace. - The
originCLI — covers most of the above:invite-member,remove-member,list-members,list-invites,revoke-invite,create-service-account,revoke-service-account,list-service-accounts,assign-role,revoke-role,current-user,current-user-grants,principal-grants,list-roles,get-role,permissions,has-permission. The audit log is GraphQL-only for now — there’s no CLI command for it yet.
Granting a role
Both directions are idempotent: assigning a role a principal already holds at that scope creates no duplicate grant, and revoking one they don’t hold is a safe no-op.
mutation AssignRole($input: IamRoleAssignmentInput!) { iam { assignRole(input: $input) }}{ "input": { "orgId": "7a07d9ec7ba2", "principal": "user:3c8e11d09f42", "projectId": "proj-4f21c9a03b17", "roleUrn": "<the roles/viewer URN from iam.roles>" }}Omit projectId for an org-wide grant. Read the role’s URN from the roles
query rather than constructing it by hand — the built-in role set is
roles/owner, roles/viewer, roles/billing, roles/iam and roles/platform.
# The built-in roles and their URNsorigin iam list-roles --org-id 7a07d9ec7ba2
origin iam assign-role \ --org-id 7a07d9ec7ba2 \ --project-id proj-4f21c9a03b17 \ --principal user:3c8e11d09f42 \ --role-urn "$ROLE_URN"Checking a permission
origin iam has-permission \ --on-resource-urn urn:origin:cloud:admin::7a07d9ec7ba2::proj-4f21c9a03b17 \ --principal user:3c8e11d09f42 \ --permission-key network.vpc.listquery HasPermission($input: IamHasPermissionInput!) { iam { hasPermission(input: $input) }}