Skip to content
GitmoapdocsBeta
Guide· How do I…?

Groups

Overview

A group is a namespace: a name in a path, a place repositories live, and the level at which you hand out access once instead of repository by repository. The top group in a chain — the one with no parent — is your organisation, and it is also the boundary the metered resources are pooled against, so storage, pipeline minutes and outbound transfer all roll up to it. Below it you nest as deeply as your teams do, granting access at whatever level makes sense and letting it flow downwards.

Use groups to give a team read access to everything it owns in one action, to keep a client's work private inside an otherwise open organisation, and to see what an organisation is consuming against what its plan allows.

Quick start

Quick start3 steps

Create a group

You’ll need an account — nothing else.
  1. Choose Create in the top bar, then New group.

  2. Give it a name — it becomes the first segment of every path inside it — and leave Parent group empty for a top-level group.

  3. Choose Create group. You are its Owner, and it is ready for repositories and subgroups.

Quick start3 steps

Add a subgroup

You’ll need Maintainer on the parent group.
  1. Open the parent group and choose New subgroup. The parent is filled in.

  2. Name it after the team or the product line it holds. Access is granted per group, so nest by who owns what.

  3. Choose Create group. It starts with its parent's visibility, and can never be more open than its parent.

Quick start3 steps

Give someone access to a group

You’ll need Maintainer on the group, and an account for the person you are adding.

Start at Members in the group's side navigation.

  1. Choose Add member.

  2. Find the person and pick a level. Reporter is the lowest level that reads a private group.

  3. Choose Add member. The access reaches every repository and subgroup beneath this group.

Before you start

  • An account with a profile.Creating an organisation needs one, and the call answers 404 if you have signed in but never completed your profile. It also has to be a personal account: a service-account token is refused with 400, whatever scopes it carries, because an organisation is owned by a person.
  • Reporter to read a private group, and Reporter on any group to read its members and its usage. A public group answers anyone, signed in or not; an internal one answers anyone signed in; a private one answers only its members, and Guest is not enough — a Guest membership on a private group still gets 404. Its members and its usage are never public, whatever the group's visibility.
  • Maintainer on the parent to nest a group under it.Creating an organisation needs no membership anywhere — it is the one group you can create from nothing — but every sub-group is created inside something you already help run.
  • Maintainer to rename a subgroup, change a group's description or set its avatar. Owner to rename an organisation, change a group's visibility or delete it.These three sit a level higher than the rest because each reaches people outside the group — and renaming an organisation gives its name up, for good once it has published anything.
  • Maintainer to manage members, and Owner to add, promote, demote or remove an Owner.You can never grant a level above your own.
  • The person you are adding already has a Gitmoap account.Adding a member resolves the account first and answers 404 when there is none. Single sign-on can create accounts on first login instead, but only once you turn automatic provisioning on and only for email domains your organisation has verified — otherwise a first login is refused unless the person was invited.
  • A token with the right scope, if you are calling the API or the CLI: group:read to read groups and their usage, group:create, group:update (which also covers the avatar), group:delete; and group_member:read, group_member:add, group_member:update, group_member:remove for membership. Scopes are fixed when a token is minted, so a script that only reports usage should carry only group:read.
  • A name that is a valid path segment.Lowercase letters, digits and hyphens, one to a hundred characters, no leading or trailing hyphen. platform-team is fine; Platform Team, platform_team and -platform are refused. An organisation's name also has to be free: not a word Gitmoap keeps, not a well-known organisation's name or one that looks like it, and not a name another organisation gave up recently — see How organisation names work.

How it works

The path is the hierarchy

Every group and every repository has one path, built by joining its name to its parent's path. Two groups under the same parent cannot share a name, and no two groups anywhere can share a full path.

acme an organisation: a group with no parent
├── platform a sub-group
│ ├── api a repository at acme/platform/api
│ └── ingest a repository at acme/platform/ingest
└── web a sub-group
└── storefront a repository at acme/web/storefront

Renaming platform rewrites the path of everything beneath it in the same request, so acme/platform/api becomes acme/core/api the moment the rename returns. The ids do not move. Anything addressed by id — a script holding a group or repository id, a pipeline definition, a token bound to the group — keeps working across a rename; anything addressed by path has to be told the new one, and that includes every git remote, because a remote names the repository by path.

How organisation names work

An organisation's name is the first segment of every address inside it, so to anyone reading a link, whoever holds a name is that name. Gitmoap keeps some names back for that reason, and checks an organisation's name against them whenever you create an organisation or rename one. A subgroup's name only has to be unique inside its parent.

A name is refused whenThe answerWhat it means
It is a word Gitmoap keeps for itself403 RESERVED_NAMESPACEWords such as admin, api, login and security, and the top-level pages of the web app. Nobody can take one
It is a well-known organisation's name403 RESERVED_NAMESPACEIt is kept for whoever proves they own that organisation's domain, and the message names the domain. You can take your reserved name by verifying it
It looks like a well-known organisation's name403 NAME_RESEMBLES_RESERVEDDigits standing in for letters (micros0ft), rn standing in for m, a hyphen inside the name (micro-soft), or a word such as official, support or inc before or after it (microsoft-support). The same domain unlocks these
Another organisation gave it up recently409 NAME_RETIREDThe message says until when, or that the name is held for good

The well-known organisations are drawn from Wikidata and the Majestic Million (Majestic-12 Ltd, CC BY 3.0): an organisation is on the list when its official website's domain is also among the most-linked domains on the web. Everyday words that happen to be brands — apple, delta, target — are kept only exactly as spelt, so apple-store is free, and names shorter than four characters, such as ibm, are kept exactly with no look-alikes refused. Places are not on the list. A name somebody already held when it joined the list stays theirs.

A name has one spelling. Names are lowercase, so Acme is never a second acme. When you look something up — a group or repository by its path, a person by username, a repository in a git clone URL — what you type is lowercased first, so Acme/Platform finds acme/platform. Creating or renaming still needs the lowercase form; put capitals in the display name.

A name you give up is held. When you rename an organisation, its old name is held for it for 90 days: nobody else can take it, and you can rename back. If the organisation has ever had a public repository or published a package, the old name is held for good instead, because people depend on what was published under it. An organisation holds at most five of its former names at a time. When an organisation is deleted, its name is held for nobody — 90 days, or for good once it published — so not even its former Owners can take it during that time. Renaming or deleting a subgroup holds nothing.

Verified domains show on the organisation. Every domain your organisation has verified under single sign-on appears on it — verified_domains on the organisation's details and in group listings — to everyone who can see the organisation, and to nobody else. It is how a reader tells your organisation from one with a similar name. A domain you have claimed but not verified never appears.

Access levels, and how they reach down

There are five levels, ordered:

LevelWhat it gets you in a group
GuestSee that a private group or repository exists, and read whatever is public or internal
ReporterRead private repositories, work items and merge requests, and file and comment on work items
DeveloperPush code, open and merge merge requests, create branches, tags and releases, trigger pipeline runs
MaintainerCreate repositories and sub-groups, manage members, rules, settings and pipeline variables
OwnerChange visibility, rename an organisation, delete, manage other Owners, read the audit log, and manage single sign-on and billing

Your effective level on any group is the highest level you hold on it or on any group above it. An Owner of acme is an Owner of acme/platform and of every repository underneath without holding a single row there, and a Developer on acme who is also a Maintainer on acme/web is a Maintainer there and a Developer everywhere else. Levels only ever add: a membership deeper in the tree can raise someone, never lower them. That is the one thing people get wrong — removing somebody from a sub-group does nothing if their access came from an ancestor.

The creator of a group is its Owner from the moment it exists, in the same operation, so a group is never left without one.

Visibility, and the rule that a child is never more open than its parent

VisibilitySigned outSigned in, not a memberMember
PublicReadReadFull, per level
Internal401ReadFull, per level
Private401404Full, per level, from Reporter up

Signed out, anything that is not public answers 401 — the platform asks for a credential before it will say anything else. Signed in, a private group answers 404 rather than 403 to anyone who is not a member, so its existence stays private too. If you were given a path and get 404, either the spelling is wrong or you are not in it — the response is deliberately the same either way. A Guest is a member: they see the group, and are told 403 rather than 404 for anything more, because it is no secret from them.

Visibility is constrained in one direction: a group can never be more visible than its parent. Asking for more is refused when you create the group and when you update it, and lowering a parent below a child that is more open is refused as well, naming the child that blocks it. Fix the named one first, then the parent.

The namespace is where the pooled limits live

Two kinds of limit apply, and they are counted in different places. Pooled resources — stored bytes, pipeline runs and minutes, outbound transfer — are counted against the organisation at the top of the chain, so every repository anywhere in the tree draws on the same budget. Counts of things — repositories, members, webhooks, service accounts — are counted against the individual group that holds them, so each group gets its own allowance.

Ask any group in the tree for its usage and you are told the organisation's pooled numbers. The group you named only decides which group's own counts come back beside them, and only the limits view carries those — the shorter usage view reports the organisation and nothing else.

How to

Create an organisation

An organisation is a group with no parent. You do not need to belong to anything to create one, only to have a profile. It starts private, and every provider Gitmoap publishes itself is installed on it at its latest version, so you can build a pipeline without installing anything first.

Choose Create in the top bar, then New group, and leave Parent group empty. Give it a name — that name becomes the first segment of every path inside it — and, if you want, a display name and description. Create group takes you to the new organisation's overview with you listed as its Owner.

Terminalbash
gitmoap groups create --group-name <group name>
curlbash
curl -X POST https://api.gitmoap.example/groups \
-H "Authorization: Bearer $GITMOAP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "group_name": "<string>" }'

Pick the name carefully. Renaming later is one action, but it changes the path of every repository underneath it, everyone with a clone has to update their remote, and the name you leave behind is held. If the name is refused, the answer says why — see How organisation names work. If it is your own company's name, kept for whoever owns your domain, create the organisation under another name first, then take your reserved name.

Add a sub-group

A sub-group is created the same way, naming the group it sits inside. It inherits its parent's visibility unless you ask for something less open, and it cannot be more open than its parent.

Open the parent group and choose New subgroup. The parent is filled in. Name it, optionally set a description, and leave visibility alone to inherit the parent's. Create group drops you into the new group.

Terminalbash
gitmoap groups create --group-name <group name>
curlbash
curl -X POST https://api.gitmoap.example/groups \
-H "Authorization: Bearer $GITMOAP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "group_name": "<string>" }'

Nest by ownership, not by repository count. A sub-group's real purpose is to be the thing you grant access to, so the useful shape is usually one sub-group per team or per product line, not one per repository.

Find the groups you can see

With no filter you get organisations — every one you can see, which for a signed-out caller means the public ones and for a signed-in caller adds the internal ones and the private ones you belong to. Name a parent and you get that group's direct children instead. You can name the parent by id or by path, but not both in the same request.

Choose Groups in the top navigation for the organisations you belong to. Inside a group, Sub-groups lists its direct children. Both lists page as you scroll and show each group's visibility beside its name.

Terminalbash
gitmoap groups list
curlbash
curl -X GET https://api.gitmoap.example/groups \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Groups you may not see are dropped from a page after it is read, so a page can come back with fewer groups than the limit you asked for while there is still more to fetch. Keep following the next-page token until there is none; a short page is not the end of the list. A page tops out at 100 groups however large a limit you ask for, so a script that asks for a thousand and reads one page has seen a tenth of them.

Open a group by its id or its path

Two ways in, same group. The id never changes; the path changes when the group or any of its ancestors is renamed.

Every group's overview lives at its path — /acme/platform in the web app is the group at acme/platform. The Copy id action on the overview gives you the id for scripts.

Terminalbash
gitmoap groups get --group-id <id>
curlbash
curl -X GET https://api.gitmoap.example/groups/{group_id} \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Paste a path into the search box, or navigate to it directly: the web app resolves paths for you, so /acme/platform and the group's id URL land in the same place.

Terminalbash
gitmoap groups get-by-path --full-path <id>
curlbash
curl -X GET https://api.gitmoap.example/groups/by-path/{full_path} \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Prefer the id in anything you store. Prefer the path in anything a person types. The CLI takes either wherever it asks for a group: a value shaped like an id is used as one, and anything else is looked up as a path.

The whole path travels as a single value, slashes and all, so let the CLI or a generated client build the request rather than pasting acme/platform into a URL by hand.

Rename a group or change its details

Display name, description and avatar reference are ordinary metadata. The group_name is not: changing it recomputes this group's path and rewrites every descendant group and repository path in the same request.

Open the group, choose Settings, then General. Edit the display name, description or name, and Save changes. Renaming asks you to confirm, because it moves everything below the group to a new path.

Terminalbash
gitmoap groups update --group-id <id>
curlbash
curl -X PUT https://api.gitmoap.example/groups/{group_id} \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Fields you leave out are left alone, so a request carrying only group_name changes only the name. To clear a display name or description, send it as an empty string rather than omitting it. If the new name is taken by a sibling group, or the new path by any other group, the rename is refused and nothing moves.

Renaming an organisation needs Owner, and its new name is checked the way a new organisation's is — except that a domain the organisation has verified unlocks the name kept for that domain. The old name is held for the organisation: for 90 days, or for good once it has published, and never more than five at a time. Sending the name the organisation already has is not a rename.

Take your reserved name

A well-known organisation's name is kept for whoever proves they own its domain, and the proof is the DNS record you publish to verify a domain under single sign-on. Verify the domain on an organisation you already have, then rename that organisation into the name.

  1. Create your organisation under any free name, or use one you already own.
  2. Claim and verify, on that organisation, the domain the refusal named — see Verify a claimed domain. A domain beneath it works too: microsoft is unlocked by microsoft.com or eu.microsoft.com, and never by a domain that only contains the name, such as microsoft.mooo.com.
  3. Rename the organisation to the name, as an Owner.

Open the organisation, choose Settings, then General, and change the name to the one kept for you. Save changes renames it, and the name it leaves behind is held for it.

Terminalbash
gitmoap groups update --group-id <id>
curlbash
curl -X PUT https://api.gitmoap.example/groups/{group_id} \
-H "Authorization: Bearer $GITMOAP_TOKEN"

The same domain unlocks the names that look like yours, so microsoft-support is yours to take as well. Only an organisation renaming itself is ever unlocked this way: a new organisation, a username and a personal group never are, which is why you create first and rename second. Releasing the domain later does not take the name back. If verifying the domain answers 501, domain verification is unavailable on Gitmoap's side — contact support@gitmoap.com.

Change who can see a group

Visibility is the one metadata change that needs Owner, and it is checked in both directions: not more open than the parent, and not more closed than a child that is already more open.

Open the group, choose Settings, then General, and pick a visibility. Anything more open than the parent is not offered. Save changes applies it.

Terminalbash
gitmoap groups update --group-id <id>
curlbash
curl -X PUT https://api.gitmoap.example/groups/{group_id} \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Going from private to internal or public exposes the group's name, description and its public contents to people who were never members. It does not open the repositories inside it — each keeps the visibility it already had. Going the other way is refused while a sub-group or repository beneath it is still more open, and the refusal names the one that blocks you.

Note

The Owner bar is on sending the visibility field, not on changing the value. A Maintainer who sends the whole group back with visibility set to what it already was is refused 403 all the same. When you are scripting a metadata edit, leave visibility out of the body unless you mean to change it.

Set or remove a group avatar

Uploading is two steps: ask for an upload URL, then send the bytes to it. The request declares the exact size of the file, and that size is bound into the URL — sending a different number of bytes is refused by the storage service, so a small declaration cannot smuggle a large upload.

Open the group, choose Settings, then General, and choose Change avatar. Pick a PNG, JPEG, GIF or WebP of at most 5 MiB. The web app requests the URL and uploads for you; the new image appears as soon as the upload finishes.

Terminalbash
gitmoap groups upload-avatar \
--group-id <id> \
--content-length <n> \
--content-type <content type>
curlbash
curl -X PUT https://api.gitmoap.example/groups/{group_id}/avatar \
-H "Authorization: Bearer $GITMOAP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "content_length": "<int64>", "content_type": "<string>" }'

The response carries two URLs: one to PUT the bytes to, which is valid for five minutes, and the address the image is served from afterwards. That second address is fixed for the life of the group, so uploading again replaces the image in place and nothing that already points at it has to be updated. Removing the avatar deletes the image, and the address stops serving one.

In Settings → General, choose Remove avatar beside the current image.

Terminalbash
gitmoap groups delete-avatar --group-id <id>
curlbash
curl -X DELETE https://api.gitmoap.example/groups/{group_id}/avatar \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Add someone to a group

Adding is create-only and names the level you want them at. Maintainer is enough for every level except Owner, which needs Owner. The request identifies the person by their account id, not by an email address, so a script adds people in two steps: look the account up by username or email, then add the id it gives back.

Open the group, choose Members, then Add member. Search for the person by username or email, pick a level, and Add. They appear in the list immediately with the level you chose.

Terminalbash
gitmoap group-members add \
--group-id <id> \
--access-level guest|reporter|developer|maintainer|owner \
--user-id <user id>
curlbash
curl -X POST https://api.gitmoap.example/groups/{group_id}/members \
-H "Authorization: Bearer $GITMOAP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "access_level": "<one of developer | guest | maintainer | owner | reporter>", "user_id": "<uuid>" }'

Three refusals are worth knowing before you hit them. The person must already have an account — there is no way to add an address that has never signed in. Adding somebody who is already a member is refused rather than silently changed; change the level instead. And each group has its own member cap for the plan, counted against that group alone, not against the organisation.

Add at the lowest level that works, but not lower. Guest lets somebody see that a private group and its repositories exist — their names, and nothing inside them. Reporter is the lowest level that reads a private group's content and the repositories in it.

See who is in a group

The list shows direct members of this group only. It does not show the people who reach it from an ancestor, which is the usual reason a group appears to have fewer members than it behaves as having. It lists people; the service accounts a group owns are listed separately and counted against a separate cap.

You need Reporter or above on the group to read its members — even when the group itself is Public or Internal. Who has access to a group is not part of what the group shows the world, so a signed-in person without a membership gets a 403, and a visitor who is not signed in is asked to sign in.

Open the group and choose Members. Type in the filter box to narrow by username or email. Each row shows the level and when the membership was created.

Terminalbash
gitmoap group-members list --group-id <id>
curlbash
curl -X GET https://api.gitmoap.example/groups/{group_id}/members \
-H "Authorization: Bearer $GITMOAP_TOKEN"

To answer who can actually touch this?, read the members of every group on the path above it as well.

Each row names the person by account id and level, not by username, so a report that has to print names resolves each id through the users API. The filter still matches on username and email even though neither comes back in the row.

Check one person's access

On the Members list, choose a row to open that person's membership, showing their level in this group and when it was granted.

Terminalbash
gitmoap group-members get --group-id <id> --user-id <id>
curlbash
curl -X GET https://api.gitmoap.example/groups/{group_id}/members/{user_id} \
-H "Authorization: Bearer $GITMOAP_TOKEN"

A 404 here means they hold no membership on this group. It is not an answer about whether they can reach it — they may hold one on an ancestor. Like the list, it needs Reporter or above on the group.

Change someone's access level

Open the group, choose Members, and pick a new level from the dropdown at the end of the row.

Terminalbash
gitmoap group-members update \
--group-id <id> \
--user-id <id> \
--access-level guest|reporter|developer|maintainer|owner
curlbash
curl -X PUT https://api.gitmoap.example/groups/{group_id}/members/{user_id} \
-H "Authorization: Bearer $GITMOAP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "access_level": "<one of developer | guest | maintainer | owner | reporter>" }'

Promoting somebody to Owner, or moving an existing Owner to anything else, needs Owner. An organisation — a top-level group — always keeps at least one Owner, so demoting its last one is refused: promote a second Owner first, then demote the first. A subgroup may be left with no direct Owner, because the organisation's Owners always administer it.

Remove someone from a group, or leave it yourself

Warning

Removing a member is immediate. Anything they hold because of that membership goes with it: access to every repository in the group and everything nested beneath it, and any work in progress they cannot reach any more. Their commits, work items, comments and merge requests stay exactly where they are — the history is not rewritten — but they lose the ability to open them if nothing else grants access. Re-adding them restores access; it does not restore anything they lost in between.

You can always remove yourself, at any level, without needing permission to manage members — with one exception: the last Owner of an organisation cannot leave it, because that would strand the organisation with nobody able to administer it. Leaving a subgroup as its last direct Owner is fine.

Open the group, choose Members, and use the menu at the end of the row to Remove from group. To leave a group yourself, use Leave group on the group's overview. Both ask you to confirm.

Terminalbash
gitmoap group-members remove --group-id <id> --user-id <id>
curlbash
curl -X DELETE https://api.gitmoap.example/groups/{group_id}/members/{user_id} \
-H "Authorization: Bearer $GITMOAP_TOKEN"

If somebody still has access after you removed them, their access is coming from a group above this one. Remove the membership where it actually lives.

See the groups you belong to

This is your own list, and only yours — asking for somebody else's memberships is refused.

Choose Groups in the top navigation. The list shows every group you hold a direct membership on, with your level in each.

Terminalbash
gitmoap group-members list-groups-for-user --user-id <id>
curlbash
curl -X GET https://api.gitmoap.example/users/{user_id}/groups \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Membership inherited from an ancestor does not create a row here, so a group you can administer through its parent will not appear. The parent will.

Check usage against your limits

Two views, different shapes. The usage view is the short one: storage, pipeline runs in flight, runs and minutes this month, and the plan the organisation is on. The limits view is the complete one: everything metered across the organisation, plus the live counts for the group you named, plus the caps that apply to individual repositories, merge requests and issues.

Open the group, choose Settings, then Usage. The cards show storage and pipeline consumption for the whole organisation against the plan's ceilings, whichever group in the tree you opened.

Terminalbash
gitmoap groups get-namespace-usage --group-id <id>
curlbash
curl -X GET https://api.gitmoap.example/groups/{group_id}/usage \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Settings → Usage also lists every metered resource in three tables: what the organisation consumes, what this group holds, and the per-resource caps that have no organisation-wide count.

Terminalbash
gitmoap groups get-namespace-limits --group-id <id>
curlbash
curl -X GET https://api.gitmoap.example/groups/{group_id}/limits \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Name any group in the tree where you are Reporter or above — the organisation-wide numbers are the same from anywhere in it, and they are never public, whatever the group's visibility. What changes is the second table, which counts this group's own repositories, members, webhooks, service accounts and the rest.

Check outbound-transfer usage

Bytes leaving the platform — clones and fetches, downloaded release assets and archives, published packages people pull — are metered per day for the whole organisation against a cap that comes from your plan.

Settings → Usage shows the current day's outbound transfer for the organisation beside the day's cap, with the window it covers.

Terminalbash
gitmoap groups get-namespace-egress-usage --group-id <id>
curlbash
curl -X GET https://api.gitmoap.example/groups/{group_id}/egress-usage \
-H "Authorization: Bearer $GITMOAP_TOKEN"

The window is a UTC day, named in the response, and it resets on its own; there is nothing to clear. A big first clone of a large repository is the usual reason a number jumps — a shallow clone (git clone --depth 1) moves far fewer bytes, and a build agent that clones on every run is worth pointing at a cache.

Delete a group

Warning

Deleting a group destroys everything attached to it, immediately and permanently. That includes every membership, every service account the group owns and their tokens, its webhooks, branch and push rules, merge-request settings, provider connections and their stored credentials, any packages published to the group, and the group's own work items, labels and milestones. There is no undo and no recycle bin. A group must be emptied of sub-groups and repositories before it can be deleted, which is the only safety net there is — everything else on the list goes without a second confirmation. Deleting an organisation also holds its name back from everybody, you included: for 90 days, or for good if it ever had a public repository or published a package.

Deleting is Owner-only and refuses while anything is still nested inside. Move or delete the repositories first, then the sub-groups, working from the leaves upwards.

Open the group, choose Settings, then General, and scroll to Delete group. The confirmation names the group and what deleting it destroys. If the group still holds sub-groups or repositories, it tells you what has to go first.

Terminalbash
gitmoap groups delete --group-id <id>
curlbash
curl -X DELETE https://api.gitmoap.example/groups/{group_id} \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Deleting an organisation gives its slot back straight away — to the account that created it, which is not necessarily the Owner doing the deleting.

Reclaim an organisation's name

This one belongs to Gitmoap's platform administrators, not to an organisation's Owner. It is how Gitmoap acts on a report that an organisation impersonates somebody, or that its name infringes a trademark or another right somebody holds in it. To make such a report, write to the legal desk through the contact form.

Warning

Reclaiming takes the name away from the organisation for good. The organisation is renamed — to a name the administrator chooses, or to renamed- followed by eight letters and digits — and every path inside it moves with it, so every git remote and every link that spelled the old name stops working. The old name is held for good: for the organisation it rightly belongs to, when the administrator names one, and otherwise for nobody. Nothing inside the organisation is deleted, and its id does not change.

The administrator records the reason, impersonation or infringement, and a note of what the reclaim rests on, and the organisation's Owners are sent the reasons before the name is taken. If the name is held for your organisation, rename your organisation to it the way you would to any free name. The other checks still apply, so a well-known organisation's name also needs its domain verified first.

There is no screen for this. A platform administrator calls it while signed in; an access token never makes anybody an administrator, whatever it carries.

Terminalbash
gitmoap groups reclaim-name \
--group-id <id> \
--note <note> \
--reason impersonation|infringement
curlbash
curl -X POST https://api.gitmoap.example/admin/groups/{group_id}/reclaim-name \
-H "Authorization: Bearer $GITMOAP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "note": "<string>", "reason": "<one of impersonation | infringement>" }'

Reference

18 endpoints

POST/groupsgroup:create

Create a new repository group

create_groupPOST /groupsOpen in the API reference

Token scope

group:create

Counts against

root_groups_per_user

Send

Body
  • avatarobjectoptional
  • descriptionobjectoptional
  • display_nameobjectoptional
  • group_namestring
  • parent_group_idobjectoptional
  • visibilityobjectoptional

Refuses

  • 400Invalid request
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden, or the name is reserved or looks like a reserved name
  • 404Parent group not found or caller has no user profile
  • 409Path already taken, or the name is held back after another organization gave it up
  • 429The root-group limit is reached; request an increase to raise it

Receive

  • avatarobjectoptional
  • created_atstring
  • created_byuuid
  • descriptionobjectoptional
  • display_nameobjectoptional
  • full_pathstring
  • group_iduuid
  • group_namestring
  • parent_group_idobjectoptional
  • updated_atstring
  • visibilityinternalprivatepublic
GET/groups/{group_id}group:read

Get a group by ID

get_groupGET /groups/{group_id}Open in the API reference

Token scope

group:read

Send

Path
  • group_iduuidGroup ID

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Group not found

Receive

No body.

GET/groups/by-path/{full_path}group:read

Get a group by its full path

get_group_by_pathGET /groups/by-path/{full_path}Open in the API reference

Token scope

group:read

Send

Path
  • full_pathstringFull hierarchical path (e.g. acme/platform)

Refuses

  • 400Invalid path
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Group not found

Receive

No body.

GET/groupsgroup:read

List groups

list_groupsGET /groupsOpen in the API reference

Token scope

group:read

Send

Query
  • parent_group_idstringoptionalFilter by parent group ID (mutually exclusive with parent_group_path)
  • parent_group_pathstringoptionalFilter by parent group path (mutually exclusive with parent_group_id)
  • limitint32optionalMaximum number of items to return
  • next_tokenstringoptionalPagination token

Refuses

  • 400Invalid request (both parent_group_id and parent_group_path provided)
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404The parent group named by `parent_group_path` does not exist

Receive

  • next_tokenobjectoptional
PUT/groups/{group_id}group:update

Update a group

update_groupPUT /groups/{group_id}Open in the API reference

Token scope

group:update

Send

Path
  • group_iduuidGroup ID
Body
  • avatarobjectoptional
  • descriptionobjectoptional
  • display_nameobjectoptional
  • group_nameobjectoptional
  • visibilityobjectoptional

Refuses

  • 400Invalid request or visibility exceeds parent
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden (Owner required for a visibility change or an organization's rename), or the new name is reserved
  • 404Group not found
  • 409New path already taken, the name is held back, or the organization holds back as many former names as it may

Receive

The same shape create_group returns — 11 fields. Go to it

Show it here
  • avatarobjectoptional
  • created_atstring
  • created_byuuid
  • descriptionobjectoptional
  • display_nameobjectoptional
  • full_pathstring
  • group_iduuid
  • group_namestring
  • parent_group_idobjectoptional
  • updated_atstring
  • visibilityinternalprivatepublic
DELETE/groups/{group_id}group:delete

Delete a group

delete_groupDELETE /groups/{group_id}Open in the API reference

Token scope

group:delete

Send

Path
  • group_iduuidGroup ID

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden (Owner required)
  • 404Group not found
  • 409Group is not empty

Receive

No body.

PUT/groups/{group_id}/avatargroup:update

Get a URL to upload a group's avatar to

upload_group_avatarPUT /groups/{group_id}/avatarOpen in the API reference

Token scope

group:update

Counts against

avatar_bytes

Send

Path
  • group_iduuidGroup ID
Body
  • content_lengthint64Exact byte size of the upload. Bound into the presigned PUT, so the upload must send exactly this many bytes; rejected up front if over the avatar cap.
  • content_typestring

Refuses

  • 400Invalid content type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden (Maintainer required)
  • 404Group not found
  • 409The request conflicts with the resource's current state; `reason` says how
  • 501Avatar uploads are not available on this deployment

Receive

  • avatar_urlstring
  • upload_urlstring
DELETE/groups/{group_id}/avatargroup:update

Delete a group avatar

delete_group_avatarDELETE /groups/{group_id}/avatarOpen in the API reference

Token scope

group:update

Send

Path
  • group_iduuidGroup ID

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden (Maintainer required)
  • 404Group not found
  • 409The request conflicts with the resource's current state; `reason` says how
  • 500Avatar upload not configured

Receive

No body.

POST/groups/{group_id}/membersgroup_member:add

Add a member to a group

add_group_memberPOST /groups/{group_id}/membersOpen in the API reference

Token scope

group_member:add

Counts against

members_per_scope

Send

Path
  • group_iduuidGroup ID
Body
  • access_leveldeveloperguestmaintainerownerreporter
  • user_iduuid

Refuses

  • 400Invalid request
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden
  • 404Group or user not found
  • 409User is already a member
  • 429Member limit reached for this group

Receive

  • access_leveldeveloperguestmaintainerownerreporter
  • created_atstring
  • group_iduuid
  • parent_group_iduuid
  • user_iduuid
  • visibilityinternalprivatepublic
GET/groups/{group_id}/members/{user_id}group_member:read

Get a specific group member

get_group_memberGET /groups/{group_id}/members/{user_id}Open in the API reference

Token scope

group_member:read

Send

Path
  • group_iduuidGroup ID
  • user_iduuidUser ID

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Authentication required — a roster is not public
  • 403Caller is not a member with at least Reporter
  • 404Group or member not found

Receive

The same shape add_group_member returns — 6 fields. Go to it

Show it here
  • access_leveldeveloperguestmaintainerownerreporter
  • created_atstring
  • group_iduuid
  • parent_group_iduuid
  • user_iduuid
  • visibilityinternalprivatepublic
GET/groups/{group_id}/membersgroup_member:read

List group members

list_group_membersGET /groups/{group_id}/membersOpen in the API reference

Token scope

group_member:read

Send

Path
  • group_iduuidGroup ID
Query
  • searchstringoptionalAutocomplete fragment — substring match on the member's username or email
  • limitint32optionalMaximum number of items to return
  • next_tokenstringoptionalPagination token

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Authentication required — a roster is not public
  • 403Caller is not a member with at least Reporter
  • 404Group not found

Receive

  • items[]object6 fields
    • access_leveldeveloperguestmaintainerownerreporter
    • created_atstring
    • group_iduuid
    • parent_group_iduuid
    • user_iduuid
    • visibilityinternalprivatepublic
  • next_tokenobjectoptional
PUT/groups/{group_id}/members/{user_id}group_member:update

Update a group member's access level

update_group_memberPUT /groups/{group_id}/members/{user_id}Open in the API reference

Token scope

group_member:update

Send

Path
  • group_iduuidGroup ID
  • user_iduuidUser ID
Body
  • access_leveldeveloperguestmaintainerownerreporter

Refuses

  • 400Invalid request
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden
  • 404Group or member not found
  • 409The organization's last Owner, or a person whose access here is a custom role

Receive

The same shape add_group_member returns — 6 fields. Go to it

Show it here
  • access_leveldeveloperguestmaintainerownerreporter
  • created_atstring
  • group_iduuid
  • parent_group_iduuid
  • user_iduuid
  • visibilityinternalprivatepublic
DELETE/groups/{group_id}/members/{user_id}group_member:remove

Remove a member from a group

remove_group_memberDELETE /groups/{group_id}/members/{user_id}Open in the API reference

Token scope

group_member:remove

Send

Path
  • group_iduuidGroup ID
  • user_iduuidUser ID

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden
  • 404Group or member not found
  • 409The organization's last Owner, or a person whose access here is a custom role

Receive

No body.

GET/users/{user_id}/groupsgroup_member:read

List groups the user is a member of

list_groups_for_userGET /users/{user_id}/groupsOpen in the API reference

Token scope

group_member:read

Send

Path
  • user_iduuidUser ID
Query
  • limitint32optionalMaximum number of items to return
  • next_tokenstringoptionalPagination token

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — not the target user
  • 404Not found — or found and private, which is masked as absent

Receive

The same shape list_group_members returns — 2 fields. Go to it

Show it here
  • items[]object6 fields
    • access_leveldeveloperguestmaintainerownerreporter
    • created_atstring
    • group_iduuid
    • parent_group_iduuid
    • user_iduuid
    • visibilityinternalprivatepublic
  • next_tokenobjectoptional
GET/groups/{group_id}/usagegroup:read

Get resource usage and plan limits for a group's namespace

get_namespace_usageGET /groups/{group_id}/usageOpen in the API reference

Token scope

group:read

Send

Path
  • group_iduuidGroup ID (usage is reported for its root namespace)

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Group not found

Receive

  • max_concurrent_pipeline_runsint32
  • max_pipeline_minutes_per_monthint32
  • max_pipeline_runs_per_monthint32
  • pipeline_minutes_this_monthint32Pipeline minutes used in the current calendar month: the running time of every pipeline step, added together.
  • pipeline_runs_in_flightint32
  • pipeline_runs_this_monthint32Runs started in the current calendar month.
  • plan_tierenterprisefreeproteamThe subscription plan an organization is on, which sets its resource limits. From smallest to largest: free, pro, team, enterprise.
  • root_group_iduuid
  • storage_bytes_limitint64
  • storage_bytes_usedint64
GET/groups/{group_id}/limitsgroup:read

Get the full usage-and-limits view for a group's namespace

get_namespace_limitsGET /groups/{group_id}/limitsOpen in the API reference

Token scope

group:read

Send

Path
  • group_iduuidGroup ID (namespace usage is reported for its root; group-scoped counts for this group)

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Group not found

Receive

  • group_counts[]object3 fields
    • limitint64
    • nameaccess_limitsanchors_per_issueanchors_per_repoapproval_rulesblueprintsbranch_rulesbranches_per_repochildren_per_issuecode_todos_per_repocomments_per_issuecomments_per_mrcommit_statuses_per_commitconcurrent_pipeline_runscriteria_per_issuecriteria_per_repocustom_rolesenvironmentslabelsmembersmembers_per_teammilestonesopen_issuesopen_mrs_per_repopackage_bytespipeline_componentspipeline_dag_nodespipeline_minutes_this_monthpipeline_runs_this_monthpipelines_per_repoprovider_connectionsprovider_installspush_bytesreactions_per_commentrelations_per_issuerelease_asset_bytesreleases_per_reporeposreviewers_per_mrscim_tokensscorecardsservice_accountsservicessso_invites_per_scopestorage_bytestags_per_repoteamsversions_per_pipeline_componentwebhookswiki_pagesWhich limit a usage-and-limits entry describes.
    • usedint64
  • group_iduuid
  • namespace[]object3 fields
    • limitint64
    • nameaccess_limitsanchors_per_issueanchors_per_repoapproval_rulesblueprintsbranch_rulesbranches_per_repochildren_per_issuecode_todos_per_repocomments_per_issuecomments_per_mrcommit_statuses_per_commitconcurrent_pipeline_runscriteria_per_issuecriteria_per_repocustom_rolesenvironmentslabelsmembersmembers_per_teammilestonesopen_issuesopen_mrs_per_repopackage_bytespipeline_componentspipeline_dag_nodespipeline_minutes_this_monthpipeline_runs_this_monthpipelines_per_repoprovider_connectionsprovider_installspush_bytesreactions_per_commentrelations_per_issuerelease_asset_bytesreleases_per_reporeposreviewers_per_mrscim_tokensscorecardsservice_accountsservicessso_invites_per_scopestorage_bytestags_per_repoteamsversions_per_pipeline_componentwebhookswiki_pagesWhich limit a usage-and-limits entry describes.
    • usedint64
  • plan_tierenterprisefreeproteamThe subscription plan an organization is on, which sets its resource limits. From smallest to largest: free, pro, team, enterprise.
  • root_group_iduuid
  • scope_limits[]object3 fields
    • limitint64
    • nameaccess_limitsanchors_per_issueanchors_per_repoapproval_rulesblueprintsbranch_rulesbranches_per_repochildren_per_issuecode_todos_per_repocomments_per_issuecomments_per_mrcommit_statuses_per_commitconcurrent_pipeline_runscriteria_per_issuecriteria_per_repocustom_rolesenvironmentslabelsmembersmembers_per_teammilestonesopen_issuesopen_mrs_per_repopackage_bytespipeline_componentspipeline_dag_nodespipeline_minutes_this_monthpipeline_runs_this_monthpipelines_per_repoprovider_connectionsprovider_installspush_bytesreactions_per_commentrelations_per_issuerelease_asset_bytesreleases_per_reporeposreviewers_per_mrscim_tokensscorecardsservice_accountsservicessso_invites_per_scopestorage_bytestags_per_repoteamsversions_per_pipeline_componentwebhookswiki_pagesWhich limit a usage-and-limits entry describes.
    • scopecommentcommitissuemerge_requestnamespacepackagepipeline_componentpushreleasereporunteamWhat a per-resource limit applies to — each repository, each merge request, each team, and so on. Usage against it is reported on that resource, not in the organization-wide view.
GET/groups/{group_id}/egress-usagegroup:read

Outbound-byte usage for a namespace, against its effective daily limit

get_namespace_egress_usageGET /groups/{group_id}/egress-usageOpen in the API reference

Token scope

group:read

Send

Path
  • group_iduuidGroup ID (usage is reported for its root namespace)

Refuses

  • 400Bad request — a path parameter is not a well-formed value of its type
  • 401Unauthenticated — no credential, or an invalid one
  • 403Forbidden — the caller's access level or token scope is insufficient
  • 404Group not found

Receive

  • namespace_dayobject3 fields
    • limit_bytesint64The limit for this window, in bytes — reported even where the limit is not enforced, so you can see what you would be held to.
    • used_bytesint64Bytes charged to this window so far.
    • windowstringThe period this covers: YYYY-MM-DDTHH for an hour, YYYY-MM-DD for a day.
  • plan_tierenterprisefreeproteamThe subscription plan an organization is on, which sets its resource limits. From smallest to largest: free, pro, team, enterprise.
  • root_group_iduuid
POST/admin/groups/{group_id}/reclaim-namenamespace_admin:manage

Reclaim an organization's name (platform admin)

reclaim_group_namePOST /admin/groups/{group_id}/reclaim-nameOpen in the API reference

Token scope

namespace_admin:manage

Send

Path
  • group_iduuidThe organization whose name is reclaimed
Body
  • hold_for_group_idobjectoptional
  • new_nameobjectoptional
  • notestringWhat a reclaim rests on — the report's reference and the right relied on, never who reported it: 1 to 2000 characters. It is kept for as long as the name is held back.
  • reasonimpersonationinfringementWhy a platform administrator reclaims an organization's name. Only these two let the name be taken at once, once its owners have been sent the reasons.

Refuses

  • 400The group is not an organization, or the request is invalid
  • 401Unauthenticated — no credential, or an invalid one
  • 403Not a platform administrator, or the new name is reserved
  • 404Group not found
  • 409The new name is taken or held back

Receive

  • groupobject11 fields
    • avatarobjectoptional
    • created_atstring
    • created_byuuid
    • descriptionobjectoptional
    • display_nameobjectoptional
    • full_pathstring
    • group_iduuid
    • group_namestring
    • parent_group_idobjectoptional
    • updated_atstring
    • visibilityinternalprivatepublic
  • held_for_group_idobjectoptional
  • reasonimpersonationinfringementWhy a platform administrator reclaims an organization's name. Only these two let the name be taken at once, once its owners have been sent the reasons.
  • reclaimed_atstringWhen the name was reclaimed.
  • released_namestring

Limits

Three caps apply directly to what this page covers. All three refuse the request outright when you reach them — nothing is half-created, and no counter moves.

LimitWhat it countsValueWhat happens at the limit
Organisations per accountGroups you create with no parent3, raisable to 1000429 TOO_MANY_REQUESTS on create. Delete one you no longer use, or ask for an increase
Members per groupPeople holding a membership on that one group50 on Free, 200 on Pro, 1000 on Team, 5000 on Enterprise; never above 50 000429 TOO_MANY_REQUESTS on add. Remove somebody, change plan, or ask for an increase
Avatar upload sizeBytes in one image5 MiB, on every plan400 BAD_REQUEST before an upload URL is issued. Resize the image

The member cap is per group, not per organisation, so a hundred-person company can run ten fifty-member teams on a plan whose cap is fifty. It counts the memberships written on that group — including the creator's Owner membership, which is there from the moment the group exists — and nothing else: people who reach the group through an ancestor do not occupy a slot, and neither do the group's service accounts, which have a cap of their own (10 on Free, 50 on Pro, 200 on Team, 1000 on Enterprise).

The organisation cap is per account and follows the person who created the group, not the group.

Three more caps shape a hierarchy and are worth knowing before you build one. Repositories and storage are reported by the usage views above; outbound transfer has its own view, because it is a rate rather than a level and is measured over a day:

LimitScopeFreeProTeamEnterprise
Repositoriesper group202002 00020 000
Storageper organisation5 GiB100 GiB1 TiB10 TiB
Outbound transferper organisation, per day5 GiB500 GiB2 TiB10 TiB

An Owner files an increase request against the organisation, naming the one limit and the value wanted, and it is reviewed before anything changes. An approved increase to a pooled or per-group limit applies to that organisation alone, and the new number appears in the limits view as soon as it is applied, because the view reports the limit enforcement actually uses. The organisations-per-account cap is the exception: it belongs to an account rather than to a namespace, so no group's limits view reports it — the only way to see where you stand is that creating one more either works or answers 429.

Troubleshooting

400 BAD_REQUESTon create, naming the name
Cause
The name is not a valid path segment — an uppercase letter, a space, an underscore, or a leading or trailing hyphen
What to do
Rewrite it as lowercase letters, digits and hyphens: platform-team, not Platform Team
400 BAD_REQUESTon any call, naming group_id
Cause
A path was pasted where an id belongs
What to do
Use the id, or the by-path endpoint. The CLI accepts either wherever it asks for a group
400 BAD_REQUESTon the by-path lookup, naming a segment
Cause
One segment of the path is not a valid name — an uppercase letter, a space, an underscore, or an empty segment from a doubled or trailing /
What to do
Check that segment against the naming rules, then let the CLI or a generated client build the request: the whole path is one value, and assembling the URL by hand is what produces the empty segments
400 BAD_REQUESTon list, naming both filters
Cause
The request sent a parent id and a parent path together
What to do
Send one of them
400 BAD_REQUEST: Child visibility cannot exceed parent visibility
Cause
You asked for a group more open than the group above it
What to do
Raise the parent's visibility first, which needs Owner on the parent, or choose the lower setting
400 BAD_REQUEST: Cannot lower group visibility … is more visible
Cause
A sub-group or repository beneath this one is more open than the level you asked for
What to do
Lower the one the message names, then retry. Work from the leaves upwards
400 BAD_REQUESTon avatar upload, naming the content type
Cause
The file is not PNG, JPEG, GIF or WebP
What to do
Convert it. SVG, HEIC and TIFF are refused
400 BAD_REQUESTon avatar upload, naming the size
Cause
The declared size is zero or above 5 MiB
What to do
Send the file's exact byte count, and resize anything larger than 5 MiB
The upload URL is accepted but the PUT to it fails
Cause
The bytes sent do not match the size declared when the URL was issued, or the five-minute URL has expired
What to do
Request a new upload URL with the true byte count and send it straight away
400 NOT_AN_ORGANIZATIONon reclaiming a name
Cause
The group, or the group the name is to be held for, is a subgroup
What to do
Name the top-level group. Only an organisation holds a name that can be reclaimed, and only an organisation can be given one
400 BAD_REQUESTon reclaiming a name, about the organisation it is held for
Cause
The name was to be held for the very organisation it is taken from
What to do
Name a different organisation to hold it for, or none
400 BAD_REQUESTon create, saying service-account callers are not supported
Cause
An organisation was created with a service-account token
What to do
Run the create with a personal token or from the web app, then add the service account to the new group and let it do the rest
401 UNAUTHORIZED
Cause
No credential at all — on a read of a group that is not public, or on any change, which always needs one — or a token that is expired or revoked
What to do
Sign in, or mint a fresh token, and send it. Only a public group answers a read with no credential; an internal or private one answers 401 before it will say whether it exists
403 FORBIDDENon creating a sub-group
Cause
Creating inside a group needs Maintainer on that group
What to do
Ask an Owner or Maintainer of the parent to raise your level, or to create it for you
403 FORBIDDENon update, where reading works
Cause
Metadata needs Maintainer, but any request that carries visibility at all needs Owner — even one that repeats the current value
What to do
Drop visibility from the body if you are not changing it. If you are, ask an Owner to make the change
403 FORBIDDENon delete
Cause
Deleting needs Owner
What to do
Ask an Owner. There is no level between Maintainer and Owner that can delete
403 PERMISSION_REQUIREDnaming scope.delete, on renaming an organisation
Cause
Renaming an organisation needs Owner, because it gives the name up
What to do
Ask an Owner to rename it. A Maintainer can still rename subgroups and change the organisation's display name
403 RESERVED_NAMESPACEon creating or renaming an organisation
Cause
The name is a word Gitmoap keeps, or a well-known organisation's name — the message says which, and names the domain it is kept for
What to do
Choose another name. If it is your own organisation's name, create the organisation under another name, verify the domain the message names, then rename — see Take your reserved name
403 NAME_RESEMBLES_RESERVEDon creating or renaming an organisation
Cause
The name looks like a well-known organisation's name — a digit for a letter, a hyphen inside it, or a word such as official or support added
What to do
Choose another name. If the name it resembles is yours, verify that organisation's domain on your organisation first, then rename
403 FORBIDDENon reclaiming a name
Cause
Reclaiming is for Gitmoap's platform administrators, signed in — an Owner cannot, and no access token can
What to do
To report impersonation or infringement, write to the legal desk through the contact form
403 FORBIDDENon adding, changing or removing a member
Cause
Managing members needs Maintainer, and anything touching an Owner needs Owner
What to do
Ask an Owner to make the change, or to promote you
403 FORBIDDENon listing somebody else's groups
Cause
That listing is yours alone
What to do
List your own memberships instead. To see who is in a group, list the group's members
403 INSUFFICIENT_SCOPE
Cause
The token is valid but was minted without the scope this call needs
What to do
Mint a new token carrying the scope — group:update to rename, group_member:add to add somebody. Scopes are chosen at mint time and cannot be added afterwards
404 NOT_FOUNDon create
Cause
With a parent named: the parent id is wrong, or the parent is private and you are not in it. With no parent: you have signed in but have no profile yet
What to do
Read which the message names. For a parent, check the id and your membership; for an account, finish your profile and create the organisation again
404 NOT_FOUNDon list, with a parent path
Cause
No group sits at that path, or it is private and you are not a member
What to do
Check the spelling segment by segment, or list by parent id instead
404 NOT_FOUNDon a group you were told exists
Cause
It is private and you are not a member, or the path is misspelled
What to do
A private group answers 404 to non-members so its existence stays private. Check the spelling, then ask to be added
404 NOT_FOUNDon add member
Cause
The person has no Gitmoap account, or the id in the URL is not theirs
What to do
Look the account up by username or email and add the id it returns. If there is no account, have them sign up — or, if you run single sign-on, invite them, or turn on automatic provisioning for a domain you have verified so their first login creates one
404 NOT_FOUNDon get, update or remove member
Cause
They hold no membership on this group
What to do
List the group's members first. They may reach the group through an ancestor instead
404 NOT_FOUNDon reclaiming a name
Cause
No group has that id, or none has the id the name is to be held for
What to do
Check both ids against the organisations' details
409 CONFLICTon create
Cause
Another group already holds that name under the same parent, or a group or repository already holds that full path — groups and repositories share one namespace
What to do
Pick another name. Nothing was created
409 CONFLICTon rename
Cause
A sibling group already holds the new name, or a group or repository already holds the new full path
What to do
Pick another name. The group keeps its old name and nothing beneath it moved
409 NAME_RETIREDon creating or renaming an organisation
Cause
Another organisation gave the name up and it is held — the message says until when, or that it is for good
What to do
Choose another name. If your own organisation renamed away from it, rename that same organisation back: a name is held for the organisation that gave it up
409 NAME_HOLD_LIMITon renaming an organisation
Cause
The organisation already holds five of its former names
What to do
Rename it back to one of them, which is always allowed, or contact support@gitmoap.com to release one
409 CONFLICTon reclaiming a name
Cause
The new name's path is taken, or no free renamed- name was found
What to do
Name a free new name explicitly and repeat. Nothing was changed
409 CONFLICTon delete: it still has child groups / it still has repositories
Cause
The group is not empty
What to do
Delete or move what is inside first, starting at the leaves. The message says which kind is blocking
409 CONFLICTon add member
Cause
They are already a member
What to do
Change their level instead of adding them again
409 CONFLICT: Cannot demote the organization's last Owner / Cannot remove the organization's last Owner
Cause
The organisation would be left with nobody able to administer it
What to do
Promote a second Owner, then repeat the change
429 TOO_MANY_REQUESTSon creating an organisation
Cause
You are at your organisation cap
What to do
Delete an organisation you no longer use, or request an increase. Nothing was created and no counter moved
429 TOO_MANY_REQUESTSon add member
Cause
This group is at its member cap for the plan
What to do
Remove a membership on this group, change plan, or request an increase. Granting the same person access from a parent group instead costs this group no slot
500 INTERNAL_ERRORon an avatar upload or delete
Cause
Image hosting is unavailable on Gitmoap's side
What to do
Contact support@gitmoap.com. Retrying will not change the result, and every other group operation is unaffected
501 NOT_IMPLEMENTEDon an avatar upload
Cause
This Gitmoap deployment does not host avatars
What to do
Set the group's avatar field to the address of a picture you host elsewhere. Every other group operation is unaffected
500 DATABASE_ERRORon any call
Cause
A transient failure on our side
What to do
Retry once. If it persists, report it with the time of the request — nothing was partially applied
Somebody you added sees a private repository in the group but cannot read its files, work items or merge requests
Cause
They were added at Guest, which shows that private content exists and reads none of it
What to do
Raise them to Reporter, the lowest level that reads private content
Somebody's access is higher than the level on their membership row
Cause
Their effective level is the highest they hold on this group or any group above it
What to do
Read the members of every group on the path above this one. The higher grant is where the access is coming from
You removed a member and they still have access
Cause
The membership that grants it lives on an ancestor group
What to do
Remove it there. Removing a row lower down cannot lower access granted higher up
The member count in the limits view is higher than the people you added
Cause
The creator's Owner membership is written when the group is created and counts like any other
What to do
Open the members list beside the count — it shows the same rows, the creator's included
A page of groups came back shorter than the limit you asked for
Cause
Groups you may not see are dropped from the page after it is read
What to do
Follow the next-page token until there is none. A short page is not the end of the list
Somebody else's organisation shows a name like yours, and yours shows your domain
Cause
The verified-domain badge is the difference: only the organisation that proved the domain shows it
What to do
Point people at the domain shown on the organisation. If the other organisation impersonates yours, report it to the legal desk through the contact form
Clone URLs stopped working after a rename
Cause
A rename rewrites the path of every repository beneath the group, and a git remote names its repository by path
What to do
Run git remote set-url against the new path in every clone. A remote cannot be addressed by id, so there is nothing to switch to that would survive the next rename
A group you created does not appear when a colleague lists groups
Cause
New organisations are private, and private groups are filtered out for people who are not members
What to do
Add them, or set the group to internal so everyone signed in can see it

FAQ

Can I move a group under a different parent?

Not as one operation — a group keeps the parent it was created with. Repositories can be transferred between groups, so the practical route is to create the group where you want it and transfer its repositories across, then delete the empty original.

What happens to a person's work when I remove them?

Nothing is deleted. Their commits, work items, comments, merge requests and releases stay exactly where they are and keep their name on them. They lose the ability to reach them, unless some other membership still grants it.

Does a sub-group need its own members?

No, and usually it should not have them. Grant at the level where the answer is the same for everything underneath, and add a sub-group membership only to raise somebody above what they inherit.

Which group do I ask for usage?

Any of them. Storage, pipeline consumption and outbound transfer are always reported for the organisation at the top of the chain. Only the per-group counts — members, repositories, webhooks — change with the group you name.

Why is a private group a 404 instead of a 403?

Because 403 would confirm it exists. For a private group, whose name may itself be sensitive, the response to a signed-in non-member is identical to the response for a group that was never created. Signed out you get 401 instead, which says nothing about the group either.

  • Repositoriescreating repositories inside a group, transferring them between groups, and repository-level members
  • Your accountlooking an account up by username or email to get the id a membership call needs
  • Access tokensminting a token with the group and group_member scopes this page needs, and revoking one
  • Service accountsnon-human identities a group owns, with their own per-group cap
  • Single sign-onconnecting an identity provider, verifying a domain — which also shows on your organisation and unlocks its reserved name — and inviting or auto-provisioning the people who sign in through it
  • Billing and planswhat each plan raises, and how to request an increase to a limit you have reached
  • Audit logwho created, renamed, deleted or re-levelled what, and when
  • Repository rulesbranch and push rules set on a group so every repository beneath it inherits them
  • Using providersthe pipeline steps a new organisation starts with, and how to install more
  • Contact Gitmoapreporting an organisation that impersonates yours or infringes your rights, through the legal desk