Skip to content

Register or update a project group

Registers the Forge project group Forge knows by client_id, or updates its name, parent and organization. Call it whenever a group is created, renamed, moved or given another organization in Forge, and once for every group that already exists, also the ones without an organization of their own: a group that is not registered ends the chain its child groups inherit through (see List project groups). Registering a deleted group's client_id again registers it again, with what you send.

All three fields, every time. Send name, parent_client_id and organization_id on every call, with null for a parent or an organization the group does not have. A rename that left one out would otherwise move the group to the top or stop billing it, so a missing field is refused with 422 VALIDATION_ERROR rather than read as null.

Choosing the organization. organization_id is the organization Forge spend inside the group is billed to: one marked selectable in organizations of List project groups. Any other is refused with 403 FORGE_PROJECT_GROUP_ORGANIZATION_NOT_SELECTABLE, whose message says why. null makes the group follow its parent: it bills to its nearest registered ancestor's organization, or to none. Keeping the organization a group already has is never checked again, so a group whose organization became unusable can still be renamed or moved (its reason keeps saying why spend cannot be billed there); registering a deleted group again checks its organization like a new one. A change applies to new spend only: spend already billed stays on the organization it was billed to, and a rented server stays billed to the organization it was rented in.

Parents. parent_client_id is Forge's id for the group this one sits in. A parent that is not registered yet is accepted: the group follows it as soon as it is registered, and until then parent_id is null and the chain stops there. A parent that would place the group inside itself is refused with 422 FORGE_PROJECT_GROUP_INVALID_PARENT, and so is nesting deeper than 32 levels, counting the groups already registered below it (so registering children before their parents is checked too).

Limit. One person may register at most 1,000 groups. Registering one more (or registering a deleted one again) is refused with 409 FORGE_PROJECT_GROUP_LIMIT_REACHED; updating a registered group is always allowed.

A refused request writes nothing. The answer is the whole list, exactly as List project groups returns it, so every group that follows this one shows its new organization at once. It names no organization and requires a credential that acts for a person (a bearer token, or a key belonging to a person); the group is yours alone.

PUThttps://api.aitronos.com/v1/forge/project-groups/{client_id}

Path Parameters

client_id string required

Forge's own id for the project group: 1 to 128 characters after surrounding spaces are removed, without control characters. An id that cannot be read is refused with VALIDATION_ERROR (details.reason invalid_client_id).

Body Parameters

name string required

The group's name, as Forge shows it: 1 to 200 characters, at least one of them visible. Surrounding whitespace is removed and every run of whitespace inside (line breaks and tabs included) becomes one space; any other control character is refused. Emoji and right-to-left text are kept as sent.

parent_client_id string or null required

The Forge id of the group it sits in, under the same rules as client_id; null at the top. The parent need not be registered yet.

organization_id string or null required

The organization its Forge spend is billed to, one marked selectable in List project groups; null to follow its parent (or bill nothing to an organization when no ancestor has one). A blank string is read as null.

Unknown fields are refused.


Returns

The whole list, exactly as List project groups returns it: title, help, unassigned_label, organizations and groups, with the group just written in place.

{
  "title": "Bill to",
  "help": "Forge spend inside this group's projects is billed to this organization: rented servers and Forge's AI features. Groups inside it follow it unless they choose their own.",
  "unassigned_label": "Not billed to an organization",
  "organizations": [
    {
      "id": "org_3c1e9a7b5d2f4e8a9b0c6d1e2f3a4b5c",
      "name": "Acme Engineering",
      "logo_url": "https://cdn.example.com/logos/acme.png",
      "selectable": true,
      "reason": null,
      "order": 0
    }
  ],
  "groups": [
    {
      "id": "fpgrp_4b9e2c7a1d3f4e8a9c0b6d2e1f3a5b7c",
      "client_id": "grp-clients",
      "name": "Clients",
      "parent_id": null,
      "parent_client_id": null,
      "organization": {"id": "org_3c1e9a7b5d2f4e8a9b0c6d1e2f3a4b5c", "name": "Acme Engineering"},
      "own_organization": {"id": "org_3c1e9a7b5d2f4e8a9b0c6d1e2f3a4b5c", "name": "Acme Engineering"},
      "inherited": false,
      "inherited_from": null,
      "billing_label": "Billed to Acme Engineering",
      "reason": null,
      "month_to_date_label": "CHF 12.40 this month",
      "created_at": "2026-10-09T08:12:03.418227Z",
      "updated_at": "2026-10-09T08:12:03.418227Z"
    },
    {
      "id": "fpgrp_9d3a6e1c5b7f4a2e8c0d4b6f1a3e5c7d",
      "client_id": "grp-acme-shop",
      "name": "Acme Shop",
      "parent_id": "fpgrp_4b9e2c7a1d3f4e8a9c0b6d2e1f3a5b7c",
      "parent_client_id": "grp-clients",
      "organization": {"id": "org_3c1e9a7b5d2f4e8a9b0c6d1e2f3a4b5c", "name": "Acme Engineering"},
      "own_organization": null,
      "inherited": true,
      "inherited_from": {"id": "fpgrp_4b9e2c7a1d3f4e8a9c0b6d2e1f3a5b7c", "client_id": "grp-clients", "name": "Clients"},
      "billing_label": "Billed to Acme Engineering, from Clients",
      "reason": null,
      "month_to_date_label": "CHF 0.00 this month",
      "created_at": "2026-10-09T08:12:04.902114Z",
      "updated_at": "2026-10-09T08:12:04.902114Z"
    }
  ]
}

Errors

StatusCodeWhen
401AUTHENTICATION_REQUIREDNo valid credential.
403API_KEY_PERSON_REQUIREDAn organization API key: it has no person of its own.
403FORGE_PROJECT_GROUP_ORGANIZATION_NOT_SELECTABLEorganization_id is a new organization for the group that it may not be billed to. message is the reason, as a sentence to show: You don't have permission to use Forge in this organization., You are not a member of this organization. or This key cannot be used in this organization.; details.organization_id is the organization sent.
409FORGE_PROJECT_GROUP_LIMIT_REACHEDYou already have 1,000 registered groups and this call would register one more. message: "You can register at most 1000 project groups. Delete one you no longer use and try again."; details.limit is 1000.
422FORGE_PROJECT_GROUP_INVALID_PARENTparent_client_id would place the group inside itself. details.reason says how: self (its own parent: "A project group cannot be placed inside itself."), cycle (one of its own groups: "A project group cannot be placed inside one of its own groups.") or too_deep (nesting past 32 levels, the groups registered below it included: "Project groups cannot be nested this deeply."). details.field is parent_client_id and details.client_id the group.
422VALIDATION_ERRORA body of the wrong shape: a missing field (parent_client_id and organization_id must be sent, as null when there is none), an unknown field, a name that is empty, longer than 200 characters or holds control characters, a parent_client_id that is empty, longer than 128 characters or holds control characters, or an organization_id longer than 255 characters. Also a client_id that cannot be read (details.reason invalid_client_id).
cURL
curl -X PUT "https://api.aitronos.com/v1/forge/project-groups/grp-acme-shop" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Shop",
    "parent_client_id": "grp-clients",
    "organization_id": null
  }'

Response:

{
  "title": "Bill to",
  "help": "Forge spend inside this group's projects is billed to this organization: rented servers and Forge's AI features. Groups inside it follow it unless they choose their own.",
  "unassigned_label": "Not billed to an organization",
  "organizations": [
    {
      "id": "org_3c1e9a7b5d2f4e8a9b0c6d1e2f3a4b5c",
      "name": "Acme Engineering",
      "logo_url": "https://cdn.example.com/logos/acme.png",
      "selectable": true,
      "reason": null,
      "order": 0
    }
  ],
  "groups": [
    {
      "id": "fpgrp_4b9e2c7a1d3f4e8a9c0b6d2e1f3a5b7c",
      "client_id": "grp-clients",
      "name": "Clients",
      "parent_id": null,
      "parent_client_id": null,
      "organization": {"id": "org_3c1e9a7b5d2f4e8a9b0c6d1e2f3a4b5c", "name": "Acme Engineering"},
      "own_organization": {"id": "org_3c1e9a7b5d2f4e8a9b0c6d1e2f3a4b5c", "name": "Acme Engineering"},
      "inherited": false,
      "inherited_from": null,
      "billing_label": "Billed to Acme Engineering",
      "reason": null,
      "month_to_date_label": "CHF 12.40 this month",
      "created_at": "2026-10-09T08:12:03.418227Z",
      "updated_at": "2026-10-09T08:12:03.418227Z"
    },
    {
      "id": "fpgrp_9d3a6e1c5b7f4a2e8c0d4b6f1a3e5c7d",
      "client_id": "grp-acme-shop",
      "name": "Acme Shop",
      "parent_id": "fpgrp_4b9e2c7a1d3f4e8a9c0b6d2e1f3a5b7c",
      "parent_client_id": "grp-clients",
      "organization": {"id": "org_3c1e9a7b5d2f4e8a9b0c6d1e2f3a4b5c", "name": "Acme Engineering"},
      "own_organization": null,
      "inherited": true,
      "inherited_from": {"id": "fpgrp_4b9e2c7a1d3f4e8a9c0b6d2e1f3a5b7c", "client_id": "grp-clients", "name": "Clients"},
      "billing_label": "Billed to Acme Engineering, from Clients",
      "reason": null,
      "month_to_date_label": "CHF 0.00 this month",
      "created_at": "2026-10-09T08:12:04.902114Z",
      "updated_at": "2026-10-09T08:12:04.902114Z"
    }
  ]
}