| 1 | --- |
| 2 | title: Organizations |
| 3 | description: Create organizations, add people by username or email, manage roles and invitations, set the base permission and picture, and share repositories. |
| 4 | --- |
| 5 | |
| 6 | An organization is a shared account that owns repositories, agents, container images and Actions secrets and variables on behalf of a group of people. Use one when more than one person works on the same code, so access doesn't depend on any one personal account. |
| 7 | |
| 8 | ## Organization roles |
| 9 | |
| 10 | Every member has one of three roles. The role decides what they can do in the organization and the role they get on every repository the organization owns: owners and admins always get `admin`, and members get the organization's [base permission](#base-permission). |
| 11 | |
| 12 | | Capability | member | admin | owner | |
| 13 | | --- | --- | --- | --- | |
| 14 | | Role on every organization repository | The base permission (`read` for new organizations) | admin | admin | |
| 15 | | See the organization's private repositories | Yes, unless the base permission is `none` | Yes | Yes | |
| 16 | | Create repositories in the organization, and get `admin` on the ones you create | Yes | Yes | Yes | |
| 17 | | Create container images in the organization and pull its images that aren't linked to a repository | Yes | Yes | Yes | |
| 18 | | Push to, delete and change the settings of an organization image that isn't linked to a repository | Only images they created | Yes | Yes | |
| 19 | | See the **People** and **Agents** tabs | Yes | Yes | Yes | |
| 20 | | **Add people** (by username or email) | No | Yes (as member or admin) | Yes (any role) | |
| 21 | | See and revoke pending invitations | No | Yes | Yes | |
| 22 | | Change roles, remove members | No | Yes, except owners | Yes, including owners | |
| 23 | | Create and manage the organization's agents | No | Yes | Yes | |
| 24 | | Manage organization Actions secrets | No | Yes | Yes | |
| 25 | | Read and manage organization Actions variables | No | Yes | Yes | |
| 26 | | Open the **Settings** tab: rename, display name, base permission, picture | No | No | Yes | |
| 27 | | Set the [agent policy](/docs/agents/organizations), and see or change the organization's model keys | No | No | Yes | |
| 28 | | Transfer repositories into the organization | No | Yes | Yes | |
| 29 | | Delete the organization (once it owns no repositories or images) | No | No | Yes | |
| 30 | | Change the display name, base permission and picture through the API | No | No | Yes | |
| 31 | | Get emails when repositories are created or deleted in the organization | No | Yes | Yes | |
| 32 | |
| 33 | Site administrators can do everything an owner can in every organization, and have admin on every repository. |
| 34 | |
| 35 | A container image linked to a repository follows that repository's roles instead (read pulls, write pushes, admin deletes), so a member who can't see a private repository can't pull its images either. See [Container registry](/docs/packages/container-registry). |
| 36 | |
| 37 | A member's role on a repository is the highest of the role their organization role gives them (the base permission for members, `admin` for owners and admins) and any role given to them directly on that repository. A direct collaborator role can raise an organization member's access (for example to `maintain` or `admin`) but never lowers it. See [Permissions](/docs/accounts/permissions) for what each repository role allows. |
| 38 | |
| 39 | ## Base permission |
| 40 | |
| 41 | The base permission is the repository role that every member of an organization gets on every repository the organization owns, like GitHub's "Base permissions" setting. It only applies to members: owners and admins always have `admin` on every organization repository, whatever the base permission is. |
| 42 | |
| 43 | | Value | Option in the web UI | What members get on every organization repository | |
| 44 | | --- | --- | --- | |
| 45 | | `none` | No permission: members see only repositories they're given | No role. Members see only public repositories, repositories they were added to as collaborators, and repositories they created. | |
| 46 | | `read` | Read: members can clone and view | `read`: view and clone, open issues and comment | |
| 47 | | `write` | Write: members can also push | `write`: also push, open and merge pull requests and run workflows | |
| 48 | | `admin` | Admin: members can also change settings | `admin`: also change settings, collaborators, branch protection, secrets and webhooks | |
| 49 | |
| 50 | `triage` and `maintain` can't be a base permission; give those roles per repository under **Settings → Collaborators**. See [Permissions](/docs/accounts/permissions) for everything each role allows. |
| 51 | |
| 52 | ### Default base permission |
| 53 | |
| 54 | - A new organization starts with `read`, as on GitHub. |
| 55 | - Organizations that existed before Diffinery added the base permission were set to `write`, so their members kept the access they already had. |
| 56 | |
| 57 | ### How the base permission combines with other access |
| 58 | |
| 59 | A member's role on an organization repository is the highest of: |
| 60 | |
| 61 | | Source | Role | |
| 62 | | --- | --- | |
| 63 | | The organization's base permission | `none`, `read`, `write` or `admin` | |
| 64 | | A collaborator role on that repository (**Settings → Collaborators**, an accepted repository invitation, or `PUT /api/v3/repos/{owner}/{repo}/collaborators/{username}`) | The role they were given | |
| 65 | | They created the repository | `admin` | |
| 66 | | The repository is public | `read` | |
| 67 | |
| 68 | A collaborator role can raise a member above the base permission but can't lower it, so to give most members less access and a few people more, set a low base permission and add those people as collaborators on the repositories they need. Diffinery has no teams. |
| 69 | |
| 70 | Any member can create repositories in the organization, whatever the base permission. As on GitHub, the member who creates a repository becomes its admin: Diffinery adds them as an `admin` collaborator, so they appear under the repository's **Settings → Collaborators**. When a member's personal agent creates the repository, the member gets that grant. Owners and admins already have `admin` everywhere, so no grant is added for them. |
| 71 | |
| 72 | The base permission also limits agents that belong to a member, because a personal agent never has more access than its owner. Organization agents aren't affected: their access comes from their **Maximum role** (see [Agents](/docs/agents/overview)). Container images that aren't linked to a repository aren't affected: every member can still create images in the organization and pull them. An image linked to a repository follows that repository's access, so with `none` a member can't pull images of private repositories they can't see, and the registry catalog leaves those images out (see [Container registry](/docs/packages/container-registry)). |
| 73 | |
| 74 | Roles are worked out on every request, so a change to the base permission applies to every member right away, including in Git and through tokens they already have. |
| 75 | |
| 76 | ### Change the base permission in the web UI |
| 77 | |
| 78 | Organization owners and site administrators: |
| 79 | |
| 80 | 1. Open the organization's page and click the **Settings** tab (`https://diffinery.com/acme?tab=settings`). |
| 81 | 2. In the **Base permission** menu, pick **No permission**, **Read**, **Write** or **Admin**. The hint under it reads "What every member can do in all of this organization's repositories. Owners and admins always have admin access, and repository settings can give people more." |
| 82 | 3. Click **Save**. You see "Organization updated." |
| 83 | |
| 84 | Admins and members can't open the **Settings** tab: they get `403 organization owners only`. The change is recorded in the audit log as `org.update`, with the new value as `base_role` (see [Audit log](/docs/security/audit-log)). |
| 85 | |
| 86 | ### Change the base permission with the API |
| 87 | |
| 88 | The API uses GitHub's field name, `default_repository_permission`. `GET /api/v3/orgs/{org}` returns the organization; members of the organization, their agents and site administrators also see `default_repository_permission` in it. Agents can also read it with the MCP tool `get_organization`. Any signed-in account can read the organization, but other accounts don't get that field. |
| 89 | |
| 90 | ```sh |
| 91 | curl https://diffinery.com/api/v3/orgs/acme \ |
| 92 | -H "Authorization: Bearer dfy_..." |
| 93 | ``` |
| 94 | |
| 95 | ```json |
| 96 | { |
| 97 | "login": "acme", |
| 98 | "type": "Organization", |
| 99 | "html_url": "https://diffinery.com/acme", |
| 100 | "avatar_url": "https://diffinery.com/api/avatars/acme", |
| 101 | "name": "Acme Inc.", |
| 102 | "default_repository_permission": "read" |
| 103 | } |
| 104 | ``` |
| 105 | |
| 106 | (Other fields of GitHub's user object, such as `id`, `node_id` and `url`, are left out here.) |
| 107 | |
| 108 | `PATCH /api/v3/orgs/{org}` changes it. Only owners and site administrators can call it, with a personal access token that has the `admin` scope and isn't restricted to repositories (or a browser session). The body takes: |
| 109 | |
| 110 | | Field | Type | Description | |
| 111 | | --- | --- | --- | |
| 112 | | `default_repository_permission` | string | `none`, `read`, `write` or `admin`. | |
| 113 | | `name` | string | The display name. An empty value clears it, and pages show the organization name instead. | |
| 114 | |
| 115 | ```sh |
| 116 | curl -X PATCH https://diffinery.com/api/v3/orgs/acme \ |
| 117 | -H "Authorization: Bearer dfy_..." \ |
| 118 | -H "Content-Type: application/json" \ |
| 119 | -d '{"default_repository_permission": "write"}' |
| 120 | ``` |
| 121 | |
| 122 | The response is the updated organization, including `default_repository_permission`. With the CLI, `diffinery api PATCH /orgs/acme -f default_repository_permission=write` sends the same request (it needs an `admin` token). There is no dedicated CLI command. |
| 123 | |
| 124 | | Status | Message | Cause | |
| 125 | | --- | --- | --- | |
| 126 | | 403 | `this token needs the admin scope` | The token's scope is `read` or `write`. | |
| 127 | | 403 | `this token is limited to repositories and can't change account settings` | The token was created with a repository list. | |
| 128 | | 403 | `only organization owners can change its settings` | You are an admin or member of the organization, not an owner. | |
| 129 | | 404 | `not found` | The organization doesn't exist, or you aren't a member of it. | |
| 130 | | 422 | `base permission must be none, read, write or admin` | `default_repository_permission` has another value. | |
| 131 | | 422 | `name must be a string` | `name` is a number, object or other non-string value. | |
| 132 | |
| 133 | Every field is checked before anything is saved, so a refused value changes nothing. |
| 134 | |
| 135 | ## Create an organization |
| 136 | |
| 137 | Any active account can create an organization and becomes its first owner. |
| 138 | |
| 139 | ### In the web UI |
| 140 | |
| 141 | 1. Go to **Settings → Organizations** (`/settings/orgs`), or click **New organization** on your dashboard. |
| 142 | 2. Under **New organization**, enter the **Organization name** (for example `acme`) and optionally a **Display name** (for example `Acme Inc.`). |
| 143 | 3. Click **Create organization**. |
| 144 | |
| 145 | Diffinery opens the new organization's **People** tab, where you can add people right away. |
| 146 | |
| 147 | ### With the API |
| 148 | |
| 149 | ```sh |
| 150 | curl -X POST https://diffinery.com/api/v3/user/orgs \ |
| 151 | -H "Authorization: Bearer dfy_..." \ |
| 152 | -H "Content-Type: application/json" \ |
| 153 | -d '{"login": "acme", "name": "Acme Inc."}' |
| 154 | ``` |
| 155 | |
| 156 | The response (`201 Created`) describes the organization, with `"type": "Organization"` and its `html_url`. |
| 157 | |
| 158 | Creating an organization through the API needs a personal access token with the `admin` scope that isn't restricted to repositories. A `read` or `write` token gets `403 this token needs the admin scope`, and a restricted token gets `403 this token is limited to repositories and can't change account settings`. In the browser no token is needed. |
| 159 | |
| 160 | ### Name rules |
| 161 | |
| 162 | The organization name is its URL (`https://diffinery.com/acme`) and the owner part of every repository URL. It shares one namespace with usernames and agent names. |
| 163 | |
| 164 | - Starts with a letter or digit, then letters, digits, `-` and `_`, up to 100 characters. Dots aren't allowed. |
| 165 | - Not already used by a person, organization or agent: "name already taken". |
| 166 | - Not reserved (for example `api`, `settings`, `new`, `login`, `admin`, `docs`): "invalid or reserved organization name". |
| 167 | |
| 168 | ## The organization page |
| 169 | |
| 170 | Open `https://diffinery.com/acme` (you need to be signed in). The header shows the organization's picture, display name, name and your role ("you are an owner", "you are an admin" or "you are a member"). The tabs are: |
| 171 | |
| 172 | | Tab | Who sees it | What it shows | |
| 173 | | --- | --- | --- | |
| 174 | | **Repositories** | Everyone signed in | Repositories you can see. Members get a **New repository** button. | |
| 175 | | **People** | Everyone signed in | Members and their roles. Owners and admins also get the **Add people** form and the **Pending invitations** list. | |
| 176 | | **Agents** | Everyone signed in | The organization's agents, their maximum role and status. Owners and admins get a **Manage agents** button. | |
| 177 | | **Settings** | Owners | The organization's picture, name, display name and [base permission](#base-permission), and its [agent policy and model keys](/docs/agents/organizations). Others get `403 organization owners only`. | |
| 178 | | **Packages** | Everyone signed in | Container images the organization owns (`/acme/-/packages`). See [Container registry](/docs/packages/container-registry). | |
| 179 | |
| 180 | Members see every repository of the organization, unless the base permission is `none`: then they see public repositories and the ones they were given access to or created. People who aren't members only see repositories that are public, or that they were added to directly. |
| 181 | |
| 182 | ## Add and remove members |
| 183 | |
| 184 | Owners and admins manage members on the **People** tab. The top of the tab shows how many members and pending invitations there are, for example "4 members · 1 pending invitation". |
| 185 | |
| 186 | ### Add people |
| 187 | |
| 188 | The **Add people** form takes a username or an email address, like GitHub's "Add people" or "Invite member" dialog: |
| 189 | |
| 190 | 1. Open `https://diffinery.com/acme?tab=people`. |
| 191 | 2. In **Username or email**, enter a username (`diffy`, or `@diffy`) or an email address (`friend@example.com`). |
| 192 | 3. Choose a **Role**: **Member** (default) or **Admin**. Owners can also choose **Owner**. |
| 193 | 4. Click **Add**. |
| 194 | |
| 195 | What happens depends on who you entered: |
| 196 | |
| 197 | | You entered | Result | |
| 198 | | --- | --- | |
| 199 | | The username of a person | Diffinery sends them an invitation, as on GitHub. You see "Invited diffy to acme as member. They join once they accept." They get an email ("@<your username> invited you to join the acme organization on Diffinery", with a **View invitation** button) and an in-app notification ("<your username> invited you to acme as member"), and become a member when they accept it under **Settings → Invitations** (see [Invitations you receive](/docs/accounts/invitations#invitations-you-receive)). If they already have a pending invitation to the organization, it offers the role you chose instead of sending another: "diffy already had a pending invitation; it now offers the admin role." | |
| 200 | | An email address | Diffinery creates an invitation and emails it, whether or not the address has an account: the form doesn't reveal who an address belongs to, and they join only by accepting. The page shows **Invitation sent to friend@example.com** with the link and a copy button: "We emailed them an invitation. You can also send them this link yourself. It works once and expires in 14 days: they join when they accept it, creating an account and passkey first if they don't have one." | |
| 201 | |
| 202 | A site administrator who enters their own username joins at once. A person whose account is waiting for an invite (pending) becomes active when you invite their email address, or when they accept an invitation sent to their username. See [Invitations](/docs/accounts/invitations) for how invitations work, including inviting from the CLI and API. |
| 203 | |
| 204 | | Message | Cause | |
| 205 | | --- | --- | |
| 206 | | "enter a username or an email address" | The field was empty. | |
| 207 | | "enter a valid email address" | The text has an `@` but isn't a complete address such as `friend@example.com`. | |
| 208 | | "no one has the username diffy; to invite someone new, enter their email address" | No account has that username. Enter their email address to invite them. | |
| 209 | | "review-bot is not a person; agents belong to an organization through Settings → Agents" | Agents and organizations can't be members. Create an organization agent instead (see [Organization agents](#organization-agents)). | |
| 210 | | "diffy's account is disabled" | A site administrator disabled the account. | |
| 211 | | "diffy is already a member of acme" | Change their role in the members table instead. | |
| 212 | | "friend@example.com already has a pending invitation; revoke it first to send a new one" | The address already has an open invitation to this organization. Letter case doesn't matter: `Friend@Example.com` counts as the same address. Revoke the invitation under **Pending invitations**, then add the address again. | |
| 213 | | "only owners can add owners" | Admins can add members and admins only. | |
| 214 | | "organization admins only" | Only owners, admins and site administrators can add people. | |
| 215 | |
| 216 | ### Pending invitations |
| 217 | |
| 218 | Owners and admins see a **Pending invitations** list on the **People** tab with each invitation's email address (or `@diffy` for an invitation sent to a username, never the person's email address), who invited them and when, the expiry date and the role. Accepted, declined and expired invitations aren't listed. |
| 219 | |
| 220 | To withdraw an invitation, click **Revoke** and confirm "Revoke the invitation for friend@example.com? The link stops working." (or "Revoke the invitation for @diffy?"). You see "Invitation revoked." Opening the revoked link shows "this invite is invalid, already used, or expired". The person isn't told. |
| 221 | |
| 222 | ### Change a member's role |
| 223 | |
| 224 | In the member's row, pick a new role (**Owner**, **Admin** or **Member**) in the **Role** menu and click **Save**. The member gets an email saying their role changed and who changed it. |
| 225 | |
| 226 | ### Remove a member |
| 227 | |
| 228 | Click **Remove** in the member's row and confirm "Remove diffy from acme?". The member loses the organization's repository access on their next request, including through their tokens, and gets an email saying they were removed. Any collaborator roles they or their agents had on the organization's repositories are removed too, including admin on repositories they created. Their personal repositories and accounts are not affected. |
| 229 | |
| 230 | Rules: |
| 231 | |
| 232 | | Rule | Message | |
| 233 | | --- | --- | |
| 234 | | Only owners can promote someone to owner, change an owner's role or remove an owner. | "only owners can manage owners" | |
| 235 | | An organization always keeps at least one owner: you can't remove or demote the last one. | "an organization needs at least one owner; make someone else an owner first" | |
| 236 | | Only owners and admins can manage members. | "organization admins only" | |
| 237 | | The username must belong to a person. | "user not found" | |
| 238 | |
| 239 | ### Leave an organization |
| 240 | |
| 241 | There is no separate **Leave** button. Ask an owner or admin to remove you. Owners and admins can remove themselves from the **People** tab, as long as another owner remains. |
| 242 | |
| 243 | ## Organization repositories |
| 244 | |
| 245 | Any member can create a repository owned by the organization. |
| 246 | |
| 247 | - **Web UI:** click **New repository** on the organization's **Repositories** tab, or choose the organization as **Owner** on the **Create a new repository** page (`/new`). |
| 248 | - **CLI:** `diffinery repo create website --org acme` (add `--public` for a public repository; the default is private). |
| 249 | - **API:** `POST /api/v3/orgs/acme/repos` with `{"name": "website", "visibility": "private"}`. The CLI and API need a token with the `write` or `admin` scope that isn't restricted to repositories; a `read` token gets `403 this token needs the write scope`. |
| 250 | |
| 251 | Access to organization repositories comes from the organization role (members get the [base permission](#base-permission), owners and admins get admin), plus any collaborators added under the repository's **Settings → Collaborators**, which can include people outside the organization and agents. A member who creates a repository is added to it as an `admin` collaborator, whatever the base permission. When a repository is created, the organization's other owners and admins get an email, and when one is deleted they are told who deleted it. |
| 252 | |
| 253 | ## Organization secrets |
| 254 | |
| 255 | Organization Actions secrets are available to workflows in every repository the organization owns, as `${{ secrets.NAME }}`. If a repository has a secret with the same name, the repository's value wins. |
| 256 | |
| 257 | Owners and admins (and site administrators) manage them with the CLI or the API; agents can't (`403 agents cannot manage secrets`). There is no page for them in the web UI yet. Listing, setting and deleting all need a personal access token with the `admin` scope that isn't restricted to repositories: other tokens get `403 this token needs the admin scope` or `403 this token is limited to repositories and can't change account settings`. |
| 258 | |
| 259 | ```sh |
| 260 | # Create or update (the value is read from a hidden prompt, stdin or --body) |
| 261 | diffinery secret set NPM_TOKEN --org acme |
| 262 | |
| 263 | # List names and when they were updated (values are never returned) |
| 264 | diffinery secret list --org acme |
| 265 | |
| 266 | # Delete |
| 267 | diffinery secret delete NPM_TOKEN --org acme |
| 268 | ``` |
| 269 | |
| 270 | | Method and path | What it does | |
| 271 | | --- | --- | |
| 272 | | `GET /api/v3/orgs/{org}/actions/secrets` | List secret names and `updated_at` | |
| 273 | | `PUT /api/v3/orgs/{org}/actions/secrets/{name}` | Create or update; body `{"value": "..."}`; returns `204` | |
| 274 | | `DELETE /api/v3/orgs/{org}/actions/secrets/{name}` | Delete; returns `204` | |
| 275 | |
| 276 | Names are stored in upper case and must match `^[A-Z_][A-Z0-9_]*$` after upper-casing, otherwise the API returns `422 invalid secret name`. Values are encrypted at rest. Deleting a secret that doesn't exist returns `404 secret not found` and isn't recorded. Each change is recorded in the [audit log](/docs/security/audit-log) as `org.secret.set` or `org.secret.delete`, with the organization as the target and the secret's name (never its value) in the detail. See [Secrets and variables](/docs/actions/secrets-and-variables) for how workflows use them. |
| 277 | |
| 278 | ## Organization variables |
| 279 | |
| 280 | Organization Actions variables are available to workflows in every repository the organization owns, as `${{ vars.NAME }}`, including repositories created later. If a repository has a variable with the same name, the repository's value wins, and an environment variable wins over both. Unlike secrets, variable values are stored and returned in plain text. |
| 281 | |
| 282 | Owners and admins (and site administrators) manage them with the REST API; there is no page for them in the web UI and no CLI command. Agents can't read or manage them, whatever their role (`403 organization admins only`, or `404 not found` when neither the agent nor its owner belongs to the organization). Members who aren't owners or admins get `403 organization admins only`. Reading needs a token of any scope; creating, changing and deleting need a personal access token with the `admin` scope that isn't restricted to repositories. |
| 283 | |
| 284 | | Method and path | What it does | |
| 285 | | --- | --- | |
| 286 | | `GET /api/v3/orgs/{org}/actions/variables` | List variables with their values, sorted by name (not paged) | |
| 287 | | `GET /api/v3/orgs/{org}/actions/variables/{name}` | One variable, or `404 variable not found` | |
| 288 | | `POST /api/v3/orgs/{org}/actions/variables` | Create or replace; body `{"name": "...", "value": "..."}`; returns `201` | |
| 289 | | `PATCH /api/v3/orgs/{org}/actions/variables/{name}` | Set the value (creating the variable if needed); body `{"value": "..."}`; returns `204` | |
| 290 | | `DELETE /api/v3/orgs/{org}/actions/variables/{name}` | Delete; returns `204` | |
| 291 | |
| 292 | ```sh |
| 293 | curl -X POST https://diffinery.com/api/v3/orgs/acme/actions/variables \ |
| 294 | -H "Authorization: Bearer dfy_..." \ |
| 295 | -H "Content-Type: application/json" \ |
| 296 | -d '{"name": "NPM_REGISTRY", "value": "https://registry.npmjs.org"}' |
| 297 | ``` |
| 298 | |
| 299 | Names must match `^[A-Za-z_][A-Za-z0-9_]*$`, can't start with `GITHUB_` (`422 invalid variable name`) and are stored in upper case. Values are strings of at most 48 KB (`422 value must be a string of at most 48 KB`). Every variable applies to all of the organization's repositories (`"visibility": "all"`); there is no per-repository selection. Each change is recorded in the [audit log](/docs/security/audit-log) as `org.variable_set` or `org.variable_delete`, with the organization as the target and the variable's name in the detail. A [GitHub organization import](/docs/migrate-from-github) adds the GitHub organization's variables that don't exist here yet. See [Secrets and variables](/docs/actions/secrets-and-variables#organization-variables) for the full details and the differences from GitHub. |
| 300 | |
| 301 | ## Organization agents |
| 302 | |
| 303 | An agent can be owned by an organization instead of a person, so it keeps working when people come and go. Owners and admins create one under **Settings → Agents** by choosing the organization as **Owner**, or with `diffinery agent create review-bot --org acme`. An organization agent can only reach that organization's repositories, and its security emails (new tokens, expiring tokens) go to the organization's owners and admins. See [Agents](/docs/agents/overview). |
| 304 | |
| 305 | ## Organization picture |
| 306 | |
| 307 | The organization's picture shows on its page, next to its repositories and in member lists. Without one, Diffinery shows the first letter or digit of the organization's name on a colored background. |
| 308 | |
| 309 | Owners change it on the **Settings** tab, in the **Profile picture** box: |
| 310 | |
| 311 | 1. Under **Upload a new picture**, choose a JPEG, PNG or WebP file of up to 5 MB. It is cropped to a centred square; a preview shows the result. |
| 312 | 2. Click **Save picture**. You see "Organization picture updated." |
| 313 | 3. To go back to initials, click **Remove picture** and confirm "Remove the picture of acme?". You see "Organization picture removed." |
| 314 | |
| 315 | Owners (and site administrators) can also change it through the API, with a token that has the `write` or `admin` scope and isn't restricted to repositories. The body is the raw image file: |
| 316 | |
| 317 | ```sh |
| 318 | curl -X PUT https://diffinery.com/api/v3/orgs/acme/avatar \ |
| 319 | -H "Authorization: Bearer dfy_..." \ |
| 320 | --data-binary @logo.png |
| 321 | |
| 322 | curl -X DELETE https://diffinery.com/api/v3/orgs/acme/avatar \ |
| 323 | -H "Authorization: Bearer dfy_..." |
| 324 | ``` |
| 325 | |
| 326 | Both return `200` with the picture's URL, for example `{"avatar_url": "https://diffinery.com/api/avatars/acme?v=1760350000"}` after an upload and `{"avatar_url": "https://diffinery.com/api/avatars/acme"}` after removing it. Errors include `403 only organization owners can change its picture` for admins and members, and `404 not found` when you aren't in the organization. See [Profile and preferences](/docs/accounts/profile-and-preferences#profile-pictures) for the limits, error messages, the public picture URL and caching. |
| 327 | |
| 328 | ## Rename an organization |
| 329 | |
| 330 | Owners open the **Settings** tab, change **Organization name**, **Display name** or **Base permission** and click **Save**. You see "Organization updated." Every field is checked before anything is saved, so a value that's refused changes nothing. If you clear **Display name**, pages show the organization name instead, and the API returns `"name": null`. The display name can also be changed with `PATCH /api/v3/orgs/{org}` and `{"name": "Acme Inc."}` (see [Change the base permission with the API](#change-the-base-permission-with-the-api)); the organization name can't. Renaming changes every repository URL in the organization, so existing clones need a new remote URL: |
| 331 | |
| 332 | ```sh |
| 333 | git remote set-url origin https://diffinery.com/acme-inc/website.git |
| 334 | ``` |
| 335 | |
| 336 | URLs with the old name keep working, like those of a [transferred repository](/docs/repositories/overview#old-urls-after-a-transfer-or-rename): web pages and the API redirect, and Git and container images are served under the old name, until another person or organization takes it. |
| 337 | |
| 338 | ## Delete an organization |
| 339 | |
| 340 | Deleting an organization removes it for good. It is allowed only once the organization owns nothing that people still use: no repositories and no container images, and no GitHub import running into it. Unlike GitHub, deleting an organization never deletes its repositories along with it: move them out first. |
| 341 | |
| 342 | ### Before you delete |
| 343 | |
| 344 | - **Who:** organization owners (and site administrators). Admins and members can't delete it. |
| 345 | - **Repositories:** [transfer](/docs/repositories/overview#transfer-a-repository) each repository to another owner, or delete it. Transferred repositories keep their old URLs working after the organization is gone. |
| 346 | - **Container images:** delete the organization's images (images linked to a repository move with it when you transfer the repository). See [Container registry](/docs/packages/container-registry). |
| 347 | - **Imports:** wait for an import into the organization to finish, or cancel it, including drafts and queued imports. |
| 348 | |
| 349 | ### Delete in the web UI |
| 350 | |
| 351 | 1. Open the organization's page and its **Settings** tab (owners only). |
| 352 | 2. In the **Danger zone**, under **Delete this organization**, type the organization's login (for example `acme`) in the confirmation box. |
| 353 | 3. Click **Delete this organization**. |
| 354 | |
| 355 | You see `Deleted acme.` and land on your home page. If you signed in more than 2 hours ago, Diffinery first asks you to [Confirm it's you](/docs/security/overview#confirming-its-you) with your passkey. |
| 356 | |
| 357 | ### Delete with the API |
| 358 | |
| 359 | ```sh |
| 360 | curl -X DELETE https://diffinery.com/api/v3/orgs/acme \ |
| 361 | -H "Authorization: Bearer dfy_..." |
| 362 | ``` |
| 363 | |
| 364 | It returns `202 Accepted` with an empty object (`{}`) once the organization is deleted. It needs a token with the `admin` scope that isn't limited to repositories. |
| 365 | |
| 366 | ### What deleting does |
| 367 | |
| 368 | - Its members, pending invitations, organization secrets and organization variables are removed. |
| 369 | - Its agents are disabled, and their tokens revoked. |
| 370 | - Its login is freed: anyone can create a user or organization with that name afterwards. Older logins from earlier renames stop redirecting. |
| 371 | - Repositories transferred out of it earlier keep their redirects: their old URLs under its name still lead to them, until a repository is created at that owner and name (see [Old URLs after a transfer or rename](/docs/repositories/overview#old-urls-after-a-transfer-or-rename)). |
| 372 | - Its page and API answer `404`. |
| 373 | - The audit log keeps the entries that name it, and records the deletion as `org.delete`. Its import history and agents still refer to it. |
| 374 | |
| 375 | Deleting can't be undone. To use the name again, create a new organization. |
| 376 | |
| 377 | ### Errors when deleting |
| 378 | |
| 379 | | Message | Cause and fix | |
| 380 | | --- | --- | |
| 381 | | `acme still owns 3 repositories; transfer or delete them before deleting the organization` (422) | Transfer or delete the repositories first. | |
| 382 | | `acme still owns 2 container images; delete them before deleting the organization` (422) | Delete the images first. | |
| 383 | | `an import into acme is still in progress; wait for it to finish or cancel it first` (422) | Wait for the import, or cancel it. | |
| 384 | | `only organization owners can delete it` (403) | You are an admin or member. Ask an owner. | |
| 385 | | `not found` (404) | The organization doesn't exist, or you aren't a member of it. | |
| 386 | | `this token needs the admin scope` (403) | Use a token with the `admin` scope. | |
| 387 | | `this token is limited to repositories and can't change account settings` (403) | Use a token that isn't restricted to repositories. | |
| 388 | | `type acme to confirm deletion` | The web confirmation box didn't match the organization's login. | |
| 389 | | `organization owners only` | Only owners can delete the organization from the web UI. | |
| 390 | |
| 391 | ## Organizations API |
| 392 | |
| 393 | | Method and path | Description | |
| 394 | | --- | --- | |
| 395 | | `GET /api/v3/user/orgs` | Organizations you belong to, each with your `role` | |
| 396 | | `POST /api/v3/user/orgs` | Create an organization: `{"login": "acme", "name": "Acme Inc."}`. Needs an `admin` token. | |
| 397 | | `GET /api/v3/orgs/{org}` | The organization; members, their agents and site administrators also get `default_repository_permission` | |
| 398 | | `PATCH /api/v3/orgs/{org}` | Change `name` or `default_repository_permission` (`none`, `read`, `write` or `admin`). Owners only; needs an `admin` token. | |
| 399 | | `DELETE /api/v3/orgs/{org}` | Delete the organization once it owns no repositories, container images or running imports; returns `202`. Owners only; needs an `admin` token. See [Delete an organization](#delete-an-organization). | |
| 400 | | `GET /api/v3/orgs/{org}/members` | Members and their roles. Members and site administrators only; others get `404`. | |
| 401 | | `POST /api/v3/orgs/{org}/invitations` | Invite by email: `{"email": "...", "role": "member"}`. Needs an `admin` token. See [Invitations](/docs/accounts/invitations#invite-with-the-api). | |
| 402 | | `GET /api/v3/orgs/{org}/invitations` | Pending invitations, each with an `id`. Owners and admins. | |
| 403 | | `DELETE /api/v3/orgs/{org}/invitations/{id}` | Revoke a pending invitation; returns `204`. Owners and admins; needs an `admin` token. | |
| 404 | | `GET /api/v3/orgs/{org}/repos` | Repositories you can see in the organization | |
| 405 | | `POST /api/v3/orgs/{org}/repos` | Create a repository in the organization. Needs a `write` or `admin` token. | |
| 406 | | `GET`, `PUT`, `DELETE /api/v3/orgs/{org}/actions/secrets[/{name}]` | Organization secrets. Needs an `admin` token. | |
| 407 | | `GET`, `POST`, `PATCH`, `DELETE /api/v3/orgs/{org}/actions/variables[/{name}]` | Organization variables. Owners and admins; changes need an `admin` token. | |
| 408 | | `PUT /api/v3/orgs/{org}/avatar` | Upload the organization's picture (raw image body). Owners only. | |
| 409 | | `DELETE /api/v3/orgs/{org}/avatar` | Remove the organization's picture. Owners only. | |
| 410 | |
| 411 | Organization objects in API responses follow GitHub's shape, with `"type": "Organization"`, `html_url` and `avatar_url`. |
| 412 | |
| 413 | Every endpoint in this table that changes something is an account-level action: besides the scope noted, the token must not be restricted to repositories, or the request gets `403 this token is limited to repositories and can't change account settings` (the picture endpoints answer `403 changing a profile picture needs a write or admin token that is not limited to repositories`). See [Personal access tokens](/docs/accounts/tokens#account-level-actions). |
| 414 | |
| 415 | Adding people by username, changing a member's role and removing a member are done in the web UI. A person accepts an invitation to an organization with `PATCH /api/v3/user/memberships/orgs/{org}` and `{"state": "active"}` (see [Invitations you receive](/docs/accounts/invitations#accept-or-decline-with-the-api)). The full list of endpoints is in the [API reference](/docs/api). |
| 416 | |
| 417 | ## How organizations differ from GitHub |
| 418 | |
| 419 | - Diffinery has three organization roles: owner, admin and member. Admins manage members, invitations, agents, secrets and variables but can't manage owners, open the organization's **Settings** tab or change the organization's picture. |
| 420 | - The base permission works like GitHub's, with the same four values (`none`, `read`, `write`, `admin`) and the same default for new organizations (`read`). Organizations created before Diffinery had the setting use `write`. |
| 421 | - A member who creates a repository gets `admin` on it, as on GitHub. |
| 422 | - There are no teams. Give extra access per repository under **Settings → Collaborators**. |
| 423 | - **Add people** works like GitHub's: enter a username or an email address. Someone added by username gets an invitation and joins when they accept it under **Settings → Invitations**. An email address gets an invitation link, which also creates the account if there is none. |
| 424 | - Organization pages need you to be signed in. |
| 425 | - Organization secrets and variables apply to every repository in the organization; there is no per-repository selection. |
| 426 | - Deleting an organization is refused while it owns repositories or container images; GitHub deletes them with it. Transfer or delete them first. |
| 427 | - Repositories can be transferred only to your own account or to an organization you own or administer, so a transfer needs no acceptance by the new owner. |