Skip to content
GitmoapdocsBeta
Guide· How do I…?

Approval rules

Overview

An approval rule says who has to approve a merge request before it can merge, and how many of them. Two people from the Release team for release/*. One code owner of whatever changed. Anyone who may approve, but a person, signed in. A merge request can merge once every rule that applies to its target branch has enough approvals that count.

Two questions are kept apart. May this person approve? is a permission: approving needs Developer or above on the repository. Does their approval count? is decided per rule when the merge is checked: whether the approver is among the rule's approvers, whether they are a person or a service account, whether they approved while signed in or with an access token, and whether they wrote or pushed the change themselves. An approval that does not count is still recorded, and the merge check says why, per approval and per rule.

Rules defined on a group apply to every repository beneath it. A rule can be enforced, so that the groups and repositories beneath may add rules and make it stricter, but never remove or weaken it.

Quick start

Quick start3 steps

Require two approvals from the Release team for every release branch in the organisation

You’ll need Owner of the organisation, and a Release team.
  1. Define the rule on the organisation: gitmoap approval-rules create-group-approval-rule --group-id acme --name "Release approvers" --applies-to-branches 'release/*' --required 2 --approvers '[{"team":"<team-id>"}]' --enforcement enforced.

  2. Open a merge request into release/2.0 in any repository of the organisation, and ask two people from the team to review it.

  3. The merge check lists Release approvers with the approvals that count so far; the merge opens at two.

Before you start

  • Maintainer on the group or repository to define, change or delete a rule, or to set the approval policy.Anyone who can see the group or repository can read its rules.
  • Owner to make a rule or the policy enforced, or to change one that is.An enforced rule is a promise that the groups and repositories beneath do not get a vote, so it stays with the people who administer access.
  • Only a group's rules can be enforced.Nothing is beneath a repository to hold to them.
  • A token with the right scope, if you are using the CLI or the API: approval_rule:read to read rules and the policy, which comes with read_api; approval_rule:manage to change them, which comes with write_api.

How it works

Which rules apply

The rules that apply to a merge request are every enforced rule on the groups above its repository, then the nearest group's or repository's own rules. A repository that defines its own rules replaces its group's; a group that defines rules replaces those of the groups above it. A rule applies to the branches its pattern names — main, release/* — or to every branch when it names none.

Who a rule accepts

A rule names its approvers with any mix of:

ApproverMeans
{ "anyone": {} }anyone who may approve merge requests in the repository
{ "person": "<id>" }one person or service account
{ "team": "<id>" }whoever is on the team when the merge is checked — someone who has left no longer counts
{ "role": { "at": { "group": "<id>" }, "min": "maintainer" } }anyone whose role at that group or repository is at least min, however it was given
{ "code_owners": {} }the owners of the files the merge request changes

It also says which kinds of approval count, in accept_from:

accept_fromCounts an approval from
people_signed_in (the default)a person approving in the web app, or in the CLI after gitmoap login — not with an access token
peoplea person, however they signed in, including with a personal access token
people_and_service_accountsa person or a service account
people_and_agentsa person, or an agent of your AI organization — only a reviewer agent can approve

Who may not approve

The approval policy of a group or repository has three switches that apply to every rule, all off by default:

SwitchWhen off, excludes
author_can_approvethe merge request's author
committers_can_approveanyone whose email authored or committed one of its commits
last_pusher_can_approvewhoever pushed its current head

and a fourth, reset_on_push: when on, an approval given at an earlier head stops counting once new commits are pushed. The policy of the nearest group or repository that has one applies, tightened by every enforced policy above it.

Everything is checked when the merge is

Nothing about an approver is frozen when they approve except how they were signed in. When the merge is checked — and again when it happens — each approval is asked whether its approver still may approve here, is still among the rule's approvers, and is not excluded by the policy. Someone removed from the team, or whose access has ended, stops counting at once.

Enforced rules can only get stricter

Beneath an enforced rule, a group or repository can strengthen it: define a rule that names it in strengthens, which applies in its place from there down. A strengthening has to be at least as strong in every field, or it is refused with 422 WOULD_WEAKEN_ENFORCED_RULE, naming the field:

FieldStronger means
requiredmore approvals
approversa subset — fewer people eligible
accept_fromnarrower: people_and_service_accounts or people_and_agents, then people, then people_signed_in — a rule may not count a kind of approval the rule above it does not, so the two wider values do not narrow each other
applies_to_branchesmore branches, or all of them

An enforced policy works the same way: beneath it, a switch that lets someone approve can only be turned off, and reset_on_push can only be turned on.

How to

See the rules and the policy that apply

The web app has no screen for this yet; use the CLI or the API.

Terminalbash
gitmoap approval-rules get-repo-approval-policy --repo-id <id>
curlbash
curl -X GET https://api.gitmoap.example/repositories/{repo_id}/approval-policy \
-H "Authorization: Bearer $GITMOAP_TOKEN"

The web app has no screen for this yet; use the CLI or the API.

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

Define a rule

The web app has no screen for this yet; use the CLI or the API.

Terminalbash
gitmoap approval-rules create-repo-approval-rule \
--repo-id <id> \
--name <name>
curlbash
curl -X POST https://api.gitmoap.example/repositories/{repo_id}/approval-rules \
-H "Authorization: Bearer $GITMOAP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "<string>" }'

The web app has no screen for this yet; use the CLI or the API.

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

Change or delete a rule

Send only what changes. Deleting an enforced rule deletes the rules that strengthen it.

Warning

Deleting a rule takes effect on the next merge check: merge requests it was holding back can merge at once. Deleting an enforced rule also deletes every rule beneath that strengthened it, and defining it again does not bring those back.

The web app has no screen for this yet; use the CLI or the API.

Terminalbash
gitmoap approval-rules update-approval-rule --rule-id <id>
curlbash
curl -X PATCH https://api.gitmoap.example/approval-rules/{rule_id} \
-H "Authorization: Bearer $GITMOAP_TOKEN"

The web app has no screen for this yet; use the CLI or the API.

Terminalbash
gitmoap approval-rules delete-approval-rule --rule-id <id>
curlbash
curl -X DELETE https://api.gitmoap.example/approval-rules/{rule_id} \
-H "Authorization: Bearer $GITMOAP_TOKEN"

Set who may not approve

The policy is set whole: send all four switches.

The web app has no screen for this yet; use the CLI or the API.

Terminalbash
gitmoap approval-rules update-repo-approval-policy \
--repo-id <id> \
--author-can-approve \
--committers-can-approve \
--last-pusher-can-approve \
--reset-on-push
curlbash
curl -X PUT https://api.gitmoap.example/repositories/{repo_id}/approval-policy \
-H "Authorization: Bearer $GITMOAP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "author_can_approve": "<boolean>", "committers_can_approve": "<boolean>", "last_pusher_can_approve": "<boolean>", "reset_on_push": "<boolean>" }'

The web app has no screen for this yet; use the CLI or the API.

Terminalbash
gitmoap approval-rules update-group-approval-policy \
--group-id <id> \
--author-can-approve \
--committers-can-approve \
--last-pusher-can-approve \
--reset-on-push
curlbash
curl -X PUT https://api.gitmoap.example/groups/{group_id}/approval-policy \
-H "Authorization: Bearer $GITMOAP_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "author_can_approve": "<boolean>", "committers_can_approve": "<boolean>", "last_pusher_can_approve": "<boolean>", "reset_on_push": "<boolean>" }'

Reference

8 endpoints

GET/groups/{group_id}/approval-policyapproval_rule:read

The approval policy and rules in force on a group.

get_group_approval_policyGET /groups/{group_id}/approval-policyOpen in the API reference

Token scope

approval_rule:read

Send

Path
  • group_iduuidGroup ID

Refuses

  • 400Invalid group id
  • 401Authentication required
  • 403The caller cannot see this group's rules, or the token lacks the scope
  • 404Group not found

Receive

  • effectiveobject4 fields
    • author_can_approvebooleanWhether a merge request's author may approve it.
    • committers_can_approvebooleanWhether someone who authored or committed a commit in it may approve it.
    • last_pusher_can_approvebooleanWhether whoever pushed its current head may approve it.
    • reset_on_pushbooleanWhether an approval stops counting when new commits are pushed.
  • policyobjectoptional
  • rules[]object12 fields
    • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inWhich kinds of approval count toward a rule.
    • applies_to_branchesobjectoptional
    • created_atdate-time
    • defined_atobject3 fields
      • full_pathstring
      • idstring
      • typegrouprepository
    • enforcementdefaultenforcedWhether the scopes beneath may replace a rule, or only strengthen it.
    • namestring
    • requiredint32Distinct approvals that must count, 1–10.
    • rule_iduuid
    • strengthensobjectoptional
    • updated_atdate-time
  • rules_here[]object7 fields
    • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inWhich kinds of approval count toward a rule.
    • applies_to_branchesobjectoptional
    • created_atdate-time
    • defined_atobject3 fields
      • full_pathstring
      • idstring
      • typegrouprepository
    • enforcementdefaultenforcedWhether the scopes beneath may replace a rule, or only strengthen it.
GET/repositories/{repo_id}/approval-policyapproval_rule:read

The approval policy and rules in force on a repository.

get_repo_approval_policyGET /repositories/{repo_id}/approval-policyOpen in the API reference

Token scope

approval_rule:read

Send

Path
  • repo_iduuidRepository ID

Refuses

  • 400Invalid repository id
  • 401Authentication required
  • 403The caller cannot see this repository's rules, or the token lacks the scope
  • 404Repository not found

Receive

The same shape get_group_approval_policy returns — 4 fields. Go to it

Show it here
  • effectiveobject4 fields
    • author_can_approvebooleanWhether a merge request's author may approve it.
    • committers_can_approvebooleanWhether someone who authored or committed a commit in it may approve it.
    • last_pusher_can_approvebooleanWhether whoever pushed its current head may approve it.
    • reset_on_pushbooleanWhether an approval stops counting when new commits are pushed.
  • policyobjectoptional
  • rules[]object12 fields
    • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inWhich kinds of approval count toward a rule.
    • applies_to_branchesobjectoptional
    • created_atdate-time
    • defined_atobject3 fields
      • full_pathstring
      • idstring
      • typegrouprepository
    • enforcementdefaultenforcedWhether the scopes beneath may replace a rule, or only strengthen it.
    • namestring
    • requiredint32Distinct approvals that must count, 1–10.
    • rule_iduuid
    • strengthensobjectoptional
    • updated_atdate-time
  • rules_here[]object7 fields
    • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inWhich kinds of approval count toward a rule.
    • applies_to_branchesobjectoptional
    • created_atdate-time
    • defined_atobject3 fields
      • full_pathstring
      • idstring
      • typegrouprepository
    • enforcementdefaultenforcedWhether the scopes beneath may replace a rule, or only strengthen it.
PUT/groups/{group_id}/approval-policyapproval_rule:manage

Set a group's approval policy — who may not approve, and whether approvals survive a push.

update_group_approval_policyPUT /groups/{group_id}/approval-policyOpen in the API reference

Token scope

approval_rule:manage

Send

Path
  • group_iduuidGroup ID
Body
  • author_can_approveboolean
  • committers_can_approveboolean
  • enforcementdefaultenforcedoptionalWhether the scopes beneath may replace a rule, or only strengthen it.
  • last_pusher_can_approveboolean
  • reset_on_pushboolean

Refuses

  • 400Invalid request
  • 401Authentication required
  • 403The caller cannot manage this group's rules, or cannot enforce them
  • 404Group not found
  • 409The request conflicts with the resource's current state; `reason` says how
  • 422A switch would be weaker than a policy enforced above allows

Receive

The same shape get_group_approval_policy returns — 4 fields. Go to it

Show it here
  • effectiveobject4 fields
    • author_can_approvebooleanWhether a merge request's author may approve it.
    • committers_can_approvebooleanWhether someone who authored or committed a commit in it may approve it.
    • last_pusher_can_approvebooleanWhether whoever pushed its current head may approve it.
    • reset_on_pushbooleanWhether an approval stops counting when new commits are pushed.
  • policyobjectoptional
  • rules[]object12 fields
    • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inWhich kinds of approval count toward a rule.
    • applies_to_branchesobjectoptional
    • created_atdate-time
    • defined_atobject3 fields
      • full_pathstring
      • idstring
      • typegrouprepository
    • enforcementdefaultenforcedWhether the scopes beneath may replace a rule, or only strengthen it.
    • namestring
    • requiredint32Distinct approvals that must count, 1–10.
    • rule_iduuid
    • strengthensobjectoptional
    • updated_atdate-time
  • rules_here[]object7 fields
    • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inWhich kinds of approval count toward a rule.
    • applies_to_branchesobjectoptional
    • created_atdate-time
    • defined_atobject3 fields
      • full_pathstring
      • idstring
      • typegrouprepository
    • enforcementdefaultenforcedWhether the scopes beneath may replace a rule, or only strengthen it.
PUT/repositories/{repo_id}/approval-policyapproval_rule:manage

Set a repository's approval policy — who may not approve, and whether approvals survive a push.

update_repo_approval_policyPUT /repositories/{repo_id}/approval-policyOpen in the API reference

Token scope

approval_rule:manage

Send

Path
  • repo_iduuidRepository ID
Body
  • author_can_approveboolean
  • committers_can_approveboolean
  • enforcementdefaultenforcedoptionalWhether the scopes beneath may replace a rule, or only strengthen it.
  • last_pusher_can_approveboolean
  • reset_on_pushboolean

Refuses

  • 400Invalid request, or enforcement asked of a repository
  • 401Authentication required
  • 403The caller cannot manage this repository's rules
  • 404Repository not found
  • 409The request conflicts with the resource's current state; `reason` says how
  • 422A switch would be weaker than a policy enforced above allows

Receive

The same shape get_group_approval_policy returns — 4 fields. Go to it

Show it here
  • effectiveobject4 fields
    • author_can_approvebooleanWhether a merge request's author may approve it.
    • committers_can_approvebooleanWhether someone who authored or committed a commit in it may approve it.
    • last_pusher_can_approvebooleanWhether whoever pushed its current head may approve it.
    • reset_on_pushbooleanWhether an approval stops counting when new commits are pushed.
  • policyobjectoptional
  • rules[]object12 fields
    • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inWhich kinds of approval count toward a rule.
    • applies_to_branchesobjectoptional
    • created_atdate-time
    • defined_atobject3 fields
      • full_pathstring
      • idstring
      • typegrouprepository
    • enforcementdefaultenforcedWhether the scopes beneath may replace a rule, or only strengthen it.
    • namestring
    • requiredint32Distinct approvals that must count, 1–10.
    • rule_iduuid
    • strengthensobjectoptional
    • updated_atdate-time
  • rules_here[]object7 fields
    • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inWhich kinds of approval count toward a rule.
    • applies_to_branchesobjectoptional
    • created_atdate-time
    • defined_atobject3 fields
      • full_pathstring
      • idstring
      • typegrouprepository
    • enforcementdefaultenforcedWhether the scopes beneath may replace a rule, or only strengthen it.
POST/groups/{group_id}/approval-rulesapproval_rule:manage

Define an approval rule on a group, for every repository beneath it.

create_group_approval_rulePOST /groups/{group_id}/approval-rulesOpen in the API reference

Token scope

approval_rule:manage

Counts against

approval_rules_per_scope

Send

Path
  • group_iduuidGroup ID
Body
  • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inoptionalWhich kinds of approval count toward a rule.
  • applies_to_branchesobjectoptional
  • enforcementdefaultenforcedoptionalWhether the scopes beneath may replace a rule, or only strengthen it.
  • namestring
  • requiredint32optionalDistinct approvals that must count, 1–10.
  • strengthensobjectoptional

Refuses

  • 400Invalid request, or `strengthens` names no enforced rule above
  • 401Authentication required
  • 403The caller cannot manage this group's rules, or cannot enforce them
  • 404Group, team or approver scope not found
  • 409This group already strengthens that rule
  • 422The rule would be weaker than the enforced rule it strengthens
  • 429Approval rule limit reached for this group

Receive

  • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inWhich kinds of approval count toward a rule.
  • applies_to_branchesobjectoptional
  • created_atdate-time
  • defined_atobject3 fields
    • full_pathstring
    • idstring
    • typegrouprepository
  • enforcementdefaultenforcedWhether the scopes beneath may replace a rule, or only strengthen it.
  • namestring
  • requiredint32Distinct approvals that must count, 1–10.
  • rule_iduuid
  • strengthensobjectoptional
  • updated_atdate-time
POST/repositories/{repo_id}/approval-rulesapproval_rule:manage

Define an approval rule on a repository.

create_repo_approval_rulePOST /repositories/{repo_id}/approval-rulesOpen in the API reference

Token scope

approval_rule:manage

Counts against

approval_rules_per_scope

Send

Path
  • repo_iduuidRepository ID
Body
  • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inoptionalWhich kinds of approval count toward a rule.
  • applies_to_branchesobjectoptional
  • enforcementdefaultenforcedoptionalWhether the scopes beneath may replace a rule, or only strengthen it.
  • namestring
  • requiredint32optionalDistinct approvals that must count, 1–10.
  • strengthensobjectoptional

Refuses

  • 400Invalid request, enforcement asked of a repository, or `strengthens` names no enforced rule above
  • 401Authentication required
  • 403The caller cannot manage this repository's rules
  • 404Repository, team or approver scope not found
  • 409This repository already strengthens that rule
  • 422The rule would be weaker than the enforced rule it strengthens
  • 429Approval rule limit reached for this repository

Receive

The same shape create_group_approval_rule returns — 10 fields. Go to it

Show it here
  • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inWhich kinds of approval count toward a rule.
  • applies_to_branchesobjectoptional
  • created_atdate-time
  • defined_atobject3 fields
    • full_pathstring
    • idstring
    • typegrouprepository
  • enforcementdefaultenforcedWhether the scopes beneath may replace a rule, or only strengthen it.
  • namestring
  • requiredint32Distinct approvals that must count, 1–10.
  • rule_iduuid
  • strengthensobjectoptional
  • updated_atdate-time
PATCH/approval-rules/{rule_id}approval_rule:manage

Change an approval rule.

update_approval_rulePATCH /approval-rules/{rule_id}Open in the API reference

Token scope

approval_rule:manage

Send

Path
  • rule_iduuidApproval rule ID
Body
  • accept_fromobjectoptional
  • applies_to_branchesobjectoptionalnull makes the rule apply to every branch.
  • enforcementobjectoptional
  • nameobjectoptional
  • requiredint32optional

Refuses

  • 400Invalid request
  • 401Authentication required
  • 403The caller cannot manage the rule's group or repository, or cannot enforce it
  • 404Approval rule, team or approver scope not found
  • 409The request conflicts with the resource's current state; `reason` says how
  • 422The rule would be weaker than the enforced rule it strengthens

Receive

The same shape create_group_approval_rule returns — 10 fields. Go to it

Show it here
  • accept_frompeoplepeople_and_agentspeople_and_service_accountspeople_signed_inWhich kinds of approval count toward a rule.
  • applies_to_branchesobjectoptional
  • created_atdate-time
  • defined_atobject3 fields
    • full_pathstring
    • idstring
    • typegrouprepository
  • enforcementdefaultenforcedWhether the scopes beneath may replace a rule, or only strengthen it.
  • namestring
  • requiredint32Distinct approvals that must count, 1–10.
  • rule_iduuid
  • strengthensobjectoptional
  • updated_atdate-time
DELETE/approval-rules/{rule_id}approval_rule:manage

Delete an approval rule.

delete_approval_ruleDELETE /approval-rules/{rule_id}Open in the API reference

Token scope

approval_rule:manage

Send

Path
  • rule_iduuidApproval rule ID

Refuses

  • 400Invalid rule id
  • 401Authentication required
  • 403The caller cannot manage the rule's group or repository, or cannot enforce it
  • 404Approval rule not found
  • 409The request conflicts with the resource's current state; `reason` says how

Receive

No body.

Limits

Approval rules per group or repository — 10 on the Free plan. A rule requires between 1 and 10 approvals and names at most 50 approvers.

PlanRules per group or repository
Free10
Pro20
Team50
Enterprise100

At the limit, defining another is refused with 429 TOO_MANY_REQUESTS. An Owner can ask for a higher limit on one namespace.

Troubleshooting

An approval shows counts: false with sign_in_required
Cause
It was made with an access token, and the rule counts people signed in
What to do
Approve in the web app or after gitmoap login, or relax the rule to people
counts: false with service_account_not_accepted
Cause
A service account approved, and the rule counts people only
What to do
Set accept_from to people_and_service_accounts if a bot's approval should count
counts: false with agent_not_accepted
Cause
An AI agent approved, and the rule does not count agents
What to do
Set accept_from to people_and_agents if a reviewer agent's approval should count
counts: false with not_an_approver
Cause
The approver is not among the rule's approvers — for a team, no longer on it
What to do
Ask someone the rule names, or change the rule
counts: false with author, committer or last_pusher
Cause
The approval policy excludes whoever wrote or pushed the change
What to do
Ask someone else to approve, or change the policy
counts: false with stale
Cause
New commits were pushed after the approval, and approvals reset on push
What to do
Ask for a fresh approval of the current head
counts: false with cannot_approve
Cause
The approver no longer has Developer or above on the repository
What to do
Ask someone who does
422— WOULD_WEAKEN_ENFORCED_RULE
Cause
A rule or policy beneath an enforced one would be weaker in the field named
What to do
Make it at least as strict, or ask an Owner above to change the enforced rule
400enforcing a repository's rule
Cause
Only a group's rules can be enforced
What to do
Define the rule on the group
403enforcing a rule
Cause
Enforcing needs Owner on the group
What to do
Ask an Owner

Examples

Count only people, signed in, from the platform team, on main

shellscript
gitmoap approval-rules create-repo-approval-rule --repo-id acme/billing --name "Platform review" \
--applies-to-branches main --required 1 --approvers '[{"team":"<team-id>"}]'

See why an approval does not count

shellscript
gitmoap merge-requests merge-check --repo-id acme/billing --mr-id 42 --output json \
| jq '.checks[] | select(.check == "approvals") | .rules[] | {name, received, required, approvals}'

Stop authors and last pushers approving across the organisation

shellscript
gitmoap approval-rules update-group-approval-policy --group-id acme --author-can-approve false \
--committers-can-approve true --last-pusher-can-approve false --reset-on-push true --enforcement enforced

FAQ

What happened to "approvals required" in the merge request settings?

It became a rule called Approvals on the same group or repository, counting approvals from anyone, from people — so an approval made with a token still counts, as it did. Its switches became the approval policy.

Can a bot's approval count?

Only where a rule accepts people_and_service_accounts. By default it is recorded and does not count.

Why do approvals from people who left the team stop counting?

Because an approval is a claim that someone eligible agreed to the change now, not that someone once was.