# Org nodes

Base URL: `https://api.brixsignage.com`. Send `Authorization: Bearer $BRIX_API_KEY` unless an operation says Auth: none.

## GET /v1/org-nodes

List locations

Returns the workspace's location tree, for example districts, regions, sites, and departments, with each location's path and member count.

Every location in the workspace tree the caller may see (not paginated). Build the tree from `parentId`.

Auth: Bearer token. Permission: `org-unit.view`.

```bash
curl "https://api.brixsignage.com/v1/org-nodes" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of Location |  |
| `data[].id` | string | Location (org node) id. |
| `data[].name` | string |  |
| `data[].parentId` | string \| null | Parent location; null for the workspace root. |
| `data[].externalId` | string \| null | Your own location code (store number, region code). Unique in the workspace. |
| `data[].timezone` | string \| null | IANA zone screens here inherit, e.g. `America/Chicago`. |
| `data[].tier` | integer | Depth: 1 = the workspace root. |
| `data[].isSpace` | boolean | True for a workspace (Space) boundary: the root, or a franchise child workspace. |
| `data[].screenCount` | integer | Screens placed directly at this location (not its children). |
| `data[].members` | array of object | People granted a role at this location. |
| `data[].members[].userId` | string |  |
| `data[].members[].userName` | string |  |
| `data[].members[].roleId` | string |  |
| `data[].members[].roleName` | string |  |
| `data[].members[].roleColor` | string \| null |  |
| `data[].members[].source` | string \| null | `sso:<connectionId>` when an identity provider granted this; null when granted in Brix. |
| `data[].featureOverrides` | any | Owner-set feature overrides, as stored (normally an array). |
| `data[].approval` | ApprovalPolicy | The location's content-approval policy, as stored. New locations inherit `{required:false, inheritFromParent:true, …}`. |
| `data[].approval.required` | boolean |  |
| `data[].approval.inheritFromParent` | boolean |  |
| `data[].approval.escalateUpTiers` | boolean |  |
| `data[].approval.allowSharedExemptions` | boolean |  |
| `data[].approval.approvers` | array of string | User ids named as approvers. |
| `data[].billing` | any | Owner-set billing metadata as stored, or null. |
| `data[].prefs` | LocationPrefs | Inheritable per-location settings (nearest ancestor wins). |
| `data[].prefs.location` | object \| null | Physical place screens here inherit (drives weather and other location-aware apps). |
| `data[].prefs.language` | string \| null | BCP 47 tag screens here inherit, e.g. `de-DE`. |
| `data[].prefs.defaultContent` | object \| null | What screens here play when they have no content of their own. |

```json
{
  "data": [
    {
      "id": "on_4d5e6f7a8b9c0d1e",
      "name": "Chicago Loop",
      "parentId": "space_1a2b3c4d5e6f7a8b",
      "externalId": "STORE-0142",
      "timezone": "America/Chicago",
      "tier": 2,
      "isSpace": false,
      "screenCount": 6,
      "members": [
        {
          "userId": "usr_5e6f7a8b9c0d1e2f",
          "userName": "Sam Rivera",
          "roleId": "role_0a1b2c3d4e5f6a7b",
          "roleName": "Store manager",
          "roleColor": null,
          "source": null
        }
      ],
      "featureOverrides": [],
      "approval": {
        "required": false,
        "inheritFromParent": true,
        "escalateUpTiers": false,
        "allowSharedExemptions": false,
        "approvers": []
      },
      "billing": null,
      "prefs": {
        "location": {
          "label": "Chicago Loop",
          "lat": 41.8837,
          "lng": -87.6289
        },
        "language": null,
        "defaultContent": null
      }
    }
  ]
}
```

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

## POST /v1/org-nodes

Create a location

Creates a new location under a parent location. Provide `name`, `parentId`, and an optional `kind`. To create many locations at once, use the bulk-create operation instead.

Auth: Bearer token. Permission: `org-unit.create`.

Request body (`application/json`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | yes | Location name. |
| `parentId` | string | no | Parent location; omit to place it directly under the workspace root. |
| `externalId` | string | no | Your own location code; must be unique in the workspace. |
| `timezone` | string | no | IANA zone, e.g. `Europe/London`. |
| `approval` | ApprovalPolicy | no | The location's content-approval policy, as stored. New locations inherit `{required:false, inheritFromParent:true, …}`. |
| `approval.required` | boolean | no |  |
| `approval.inheritFromParent` | boolean | no |  |
| `approval.escalateUpTiers` | boolean | no |  |
| `approval.allowSharedExemptions` | boolean | no |  |
| `approval.approvers` | array of string | no | User ids named as approvers. |
| `isSpace` | boolean | no | Owner-only: create a child workspace (franchise). |
| `billingOwnerNodeId` | string | no | With `isSpace`: the ancestor workspace that pays. |
| `screenLimit` | integer | no | Owner-only, advisory. |
| `tier` | integer | no | Owner-only. Defaults to 2. |
| `billing` | any | no | Owner-only. |
| `featureOverrides` | any | no | Owner-only. |

```bash
curl -X POST "https://api.brixsignage.com/v1/org-nodes" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Chicago Loop","parentId":"on_0a1b2c3d4e5f6a7b","externalId":"STORE-0142","timezone":"America/Chicago"}'
```

Response 201: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | Location | A location in the workspace tree (an org node). |
| `data.id` | string | Location (org node) id. |
| `data.name` | string |  |
| `data.parentId` | string \| null | Parent location; null for the workspace root. |
| `data.externalId` | string \| null | Your own location code (store number, region code). Unique in the workspace. |
| `data.timezone` | string \| null | IANA zone screens here inherit, e.g. `America/Chicago`. |
| `data.tier` | integer | Depth: 1 = the workspace root. |
| `data.isSpace` | boolean | True for a workspace (Space) boundary: the root, or a franchise child workspace. |
| `data.screenCount` | integer | Screens placed directly at this location (not its children). |
| `data.members` | array of object | People granted a role at this location. |
| `data.members[].userId` | string |  |
| `data.members[].userName` | string |  |
| `data.members[].roleId` | string |  |
| `data.members[].roleName` | string |  |
| `data.members[].roleColor` | string \| null |  |
| `data.members[].source` | string \| null | `sso:<connectionId>` when an identity provider granted this; null when granted in Brix. |
| `data.featureOverrides` | any | Owner-set feature overrides, as stored (normally an array). |
| `data.approval` | ApprovalPolicy | The location's content-approval policy, as stored. New locations inherit `{required:false, inheritFromParent:true, …}`. |
| `data.approval.required` | boolean |  |
| `data.approval.inheritFromParent` | boolean |  |
| `data.approval.escalateUpTiers` | boolean |  |
| `data.approval.allowSharedExemptions` | boolean |  |
| `data.approval.approvers` | array of string | User ids named as approvers. |
| `data.billing` | any | Owner-set billing metadata as stored, or null. |
| `data.prefs` | LocationPrefs | Inheritable per-location settings (nearest ancestor wins). |
| `data.prefs.location` | object \| null | Physical place screens here inherit (drives weather and other location-aware apps). |
| `data.prefs.language` | string \| null | BCP 47 tag screens here inherit, e.g. `de-DE`. |
| `data.prefs.defaultContent` | object \| null | What screens here play when they have no content of their own. |

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: An owner-only field was set (`isSpace`, `billing`, `featureOverrides`, `tier`, `screenLimit`, or a non-inheriting approval policy).

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 409: `location_id_taken`: another location already uses this `externalId`.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 422: `name` missing, invalid `externalId`, or `parentId` not found.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

## GET /v1/org-nodes/{id}

Get a location

Returns one location, including its path, parent, and settings.

Auth: Bearer token. Permission: `org-unit.view`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Location (org node) id. |

```bash
curl "https://api.brixsignage.com/v1/org-nodes/{id}" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | Location | A location in the workspace tree (an org node). |
| `data.id` | string | Location (org node) id. |
| `data.name` | string |  |
| `data.parentId` | string \| null | Parent location; null for the workspace root. |
| `data.externalId` | string \| null | Your own location code (store number, region code). Unique in the workspace. |
| `data.timezone` | string \| null | IANA zone screens here inherit, e.g. `America/Chicago`. |
| `data.tier` | integer | Depth: 1 = the workspace root. |
| `data.isSpace` | boolean | True for a workspace (Space) boundary: the root, or a franchise child workspace. |
| `data.screenCount` | integer | Screens placed directly at this location (not its children). |
| `data.members` | array of object | People granted a role at this location. |
| `data.members[].userId` | string |  |
| `data.members[].userName` | string |  |
| `data.members[].roleId` | string |  |
| `data.members[].roleName` | string |  |
| `data.members[].roleColor` | string \| null |  |
| `data.members[].source` | string \| null | `sso:<connectionId>` when an identity provider granted this; null when granted in Brix. |
| `data.featureOverrides` | any | Owner-set feature overrides, as stored (normally an array). |
| `data.approval` | ApprovalPolicy | The location's content-approval policy, as stored. New locations inherit `{required:false, inheritFromParent:true, …}`. |
| `data.approval.required` | boolean |  |
| `data.approval.inheritFromParent` | boolean |  |
| `data.approval.escalateUpTiers` | boolean |  |
| `data.approval.allowSharedExemptions` | boolean |  |
| `data.approval.approvers` | array of string | User ids named as approvers. |
| `data.billing` | any | Owner-set billing metadata as stored, or null. |
| `data.prefs` | LocationPrefs | Inheritable per-location settings (nearest ancestor wins). |
| `data.prefs.location` | object \| null | Physical place screens here inherit (drives weather and other location-aware apps). |
| `data.prefs.language` | string \| null | BCP 47 tag screens here inherit, e.g. `de-DE`. |
| `data.prefs.defaultContent` | object \| null | What screens here play when they have no content of their own. |

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: The token lacks the permission this operation needs (see `x-brix-permission`).

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 404: No such location in this workspace.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

## PATCH /v1/org-nodes/{id}

Update a location

Updates a location's name, parent location, approval configuration, or preferences. Moving a location to a new parent updates the path of every location beneath it.

Rename, re-code, move, or change inheritable settings. Only the fields sent change.

Auth: Bearer token. Permission: `org-unit.edit`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Location (org node) id. |

Request body (`application/json`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | no |  |
| `parentId` | string \| null | no | Move under another location (null = the workspace root). |
| `externalId` | string \| null | no |  |
| `timezone` | string \| null | no | IANA zone, or null/empty to inherit. |
| `tier` | integer | no |  |
| `prefs` | object | no | Per-key: a value sets it, null clears it back to inherit, absent keeps it. |
| `prefs.location` | object \| null | no |  |
| `prefs.language` | string \| null | no | BCP 47 tag, or null to inherit. |
| `prefs.defaultContent` | object \| null | no |  |
| `approval` | ApprovalPolicy | no | The location's content-approval policy, as stored. New locations inherit `{required:false, inheritFromParent:true, …}`. |
| `approval.required` | boolean | no |  |
| `approval.inheritFromParent` | boolean | no |  |
| `approval.escalateUpTiers` | boolean | no |  |
| `approval.allowSharedExemptions` | boolean | no |  |
| `approval.approvers` | array of string | no | User ids named as approvers. |
| `approvalBase` | ApprovalPolicy | no | The location's content-approval policy, as stored. New locations inherit `{required:false, inheritFromParent:true, …}`. |
| `approvalBase.required` | boolean | no |  |
| `approvalBase.inheritFromParent` | boolean | no |  |
| `approvalBase.escalateUpTiers` | boolean | no |  |
| `approvalBase.allowSharedExemptions` | boolean | no |  |
| `approvalBase.approvers` | array of string | no | User ids named as approvers. |
| `billing` | any | no | Owner-only. |
| `featureOverrides` | any | no | Owner-only. |

```bash
curl -X PATCH "https://api.brixsignage.com/v1/org-nodes/{id}" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | Location | A location in the workspace tree (an org node). |
| `data.id` | string | Location (org node) id. |
| `data.name` | string |  |
| `data.parentId` | string \| null | Parent location; null for the workspace root. |
| `data.externalId` | string \| null | Your own location code (store number, region code). Unique in the workspace. |
| `data.timezone` | string \| null | IANA zone screens here inherit, e.g. `America/Chicago`. |
| `data.tier` | integer | Depth: 1 = the workspace root. |
| `data.isSpace` | boolean | True for a workspace (Space) boundary: the root, or a franchise child workspace. |
| `data.screenCount` | integer | Screens placed directly at this location (not its children). |
| `data.members` | array of object | People granted a role at this location. |
| `data.members[].userId` | string |  |
| `data.members[].userName` | string |  |
| `data.members[].roleId` | string |  |
| `data.members[].roleName` | string |  |
| `data.members[].roleColor` | string \| null |  |
| `data.members[].source` | string \| null | `sso:<connectionId>` when an identity provider granted this; null when granted in Brix. |
| `data.featureOverrides` | any | Owner-set feature overrides, as stored (normally an array). |
| `data.approval` | ApprovalPolicy | The location's content-approval policy, as stored. New locations inherit `{required:false, inheritFromParent:true, …}`. |
| `data.approval.required` | boolean |  |
| `data.approval.inheritFromParent` | boolean |  |
| `data.approval.escalateUpTiers` | boolean |  |
| `data.approval.allowSharedExemptions` | boolean |  |
| `data.approval.approvers` | array of string | User ids named as approvers. |
| `data.billing` | any | Owner-set billing metadata as stored, or null. |
| `data.prefs` | LocationPrefs | Inheritable per-location settings (nearest ancestor wins). |
| `data.prefs.location` | object \| null | Physical place screens here inherit (drives weather and other location-aware apps). |
| `data.prefs.language` | string \| null | BCP 47 tag screens here inherit, e.g. `de-DE`. |
| `data.prefs.defaultContent` | object \| null | What screens here play when they have no content of their own. |

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: Owner-only field, or a move you lack permission for at the destination.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 404: No such location in this workspace.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 409: `externalId` already used, or `approvalBase` no longer matches.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 422: Invalid field (a malformed `prefs`, moving a location under itself, unplayable default content).

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

## DELETE /v1/org-nodes/{id}

Delete location

Soft-deletes a location. Everything at that location, including screens, content and folders, is transferred to its parent location first, so nothing is left orphaned. Role assignments at the location are removed, so the same rule as removing a role assignment applies: only an account owner can remove an owner's assignment. The root location and any location with active child locations cannot be deleted.

**Notes.**
- Screens and content move to the parent. The people placed here lose their role at this location.

Auth: Bearer token. Permission: `org-unit.delete`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Location (org node) id. |

```bash
curl -X DELETE "https://api.brixsignage.com/v1/org-nodes/{id}" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string |  |
| `data.deleted` | true |  |
| `data.transferredTo` | string | The parent location that received this location's screens and content. |

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: You do not hold `org-unit.delete` there, or a person placed there outranks you (`owner_required`, `outranked`).

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 404: No such location in this workspace.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 409: `protected` (the workspace root), `has_children` (move or delete the child locations first), or `last_owner`.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 502: A franchise workspace's own subscription could not be ended; nothing was deleted.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

## GET /v1/org-nodes/{id}/members

List location members

Returns the people directly assigned a role at this specific location, including the id of each role assignment. This id is needed to remove a membership, and is not included in the general roster listing.

**Notes.**
- Only the roles granted at this location, not those inherited from a parent.

Auth: Bearer token. Permission: `user.view`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Location (org node) id. |

```bash
curl "https://api.brixsignage.com/v1/org-nodes/{id}/members" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | array of object |  |
| `data[].id` | string | Membership id. |
| `data[].userId` | string |  |
| `data[].userName` | string \| null |  |
| `data[].roleId` | string |  |
| `data[].roleName` | string \| null |  |
| `data[].roleColor` | string \| null |  |

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: You do not hold `user.view` at this location.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 404: No such location in this workspace.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

## POST /v1/org-nodes/{id}/members

Assign role at location

Grants a person a role at this location. Provide `userId` and `roleId`. You cannot grant a role carrying permissions you do not hold yourself at that location.

**Notes.**
- The 201 body is the membership row as written.

Auth: Bearer token. Permission: `user.edit`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Location (org node) id. |

Request body (`application/json`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `userId` | string | yes |  |
| `roleId` | string | yes |  |

```bash
curl -X POST "https://api.brixsignage.com/v1/org-nodes/{id}/members" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json"
```

Response 201: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string | Membership id. |
| `data.spaceId` | string |  |
| `data.nodeId` | string |  |
| `data.userId` | string |  |
| `data.roleId` | string |  |
| `data.createdAt` | string | ISO-8601 timestamp (UTC). |

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: The role holds permissions you do not hold, or you cannot grant them at this location.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 404: No such location, person or role in this workspace.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 422: `userId` or `roleId` is missing.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

## DELETE /v1/org-nodes/{id}/members/{memberId}

Remove role assignment

Removes one person's role assignment at this location. Only an account owner can remove the assignment of a person who is an owner at this location, and you cannot remove an assignment that carries a permission you do not hold here. An API key is never an owner. Refuses the request if it would leave the workspace without any account owner.

Auth: Bearer token. Permission: `user.edit`.

Parameters:

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | Location (org node) id. |
| `memberId` | path | string | yes | Membership id (from the member list). |

```bash
curl -X DELETE "https://api.brixsignage.com/v1/org-nodes/{id}/members/{memberId}" \
  -H "Authorization: Bearer $BRIX_API_KEY"
```

Response 200: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.id` | string |  |
| `data.removed` | true |  |

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: You do not hold `user.edit` here, or the person outranks you (`owner_required`, `outranked`).

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 404: No such location or membership.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 409: `last_owner`: this is the last owner of the workspace.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

## POST /v1/org-nodes/bulk

Bulk-create locations

Creates up to 200 locations in a single call, for example to paste in a full location list at fleet setup. Send `parentId` and either a list of `names` or a list of `units`, each with a name and an optional time zone, town, Location ID and parent Location ID, so one list can hold regions and the stores under them. Empty or repeated names, and Location IDs already in use, are skipped, so the same list can safely be submitted again. Requires the create-location permission at the parent location.

**Notes.**
- Idempotent: sending the same list again creates nothing new and lists every row in `skipped`.

Auth: Bearer token. Permission: `org-unit.create`.

Request body (`application/json`):

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `parentId` | string | yes | The location the new ones go under (unless a row names its own parent). |
| `names` | array of string | no | Location names. Use this or `units`. |
| `units` | array of object | no | One row per location (up to 200). Use this or `names`. |
| `units[].name` | string | yes |  |
| `units[].timezone` | string \| null | no | IANA zone. |
| `units[].location` | object \| null | no | A town or city (with coordinates, or looked up from `label`). |
| `units[].externalId` | string | no | Your own location code. |
| `units[].parentExternalId` | string | no | The location code of this row's parent: an earlier row, or an existing location. |

```bash
curl -X POST "https://api.brixsignage.com/v1/org-nodes/bulk" \
  -H "Authorization: Bearer $BRIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"parentId":"on_0a1b2c3d4e5f6a7b","units":[{"name":"Chicago Loop","timezone":"America/Chicago","externalId":"STORE-0142"}]}'
```

Response 201: Success.

| Field | Type | Description |
| --- | --- | --- |
| `data` | object |  |
| `data.created` | array of Location |  |
| `data.created[].id` | string | Location (org node) id. |
| `data.created[].name` | string |  |
| `data.created[].parentId` | string \| null | Parent location; null for the workspace root. |
| `data.created[].externalId` | string \| null | Your own location code (store number, region code). Unique in the workspace. |
| `data.created[].timezone` | string \| null | IANA zone screens here inherit, e.g. `America/Chicago`. |
| `data.created[].tier` | integer | Depth: 1 = the workspace root. |
| `data.created[].isSpace` | boolean | True for a workspace (Space) boundary: the root, or a franchise child workspace. |
| `data.created[].screenCount` | integer | Screens placed directly at this location (not its children). |
| `data.created[].members` | array of object | People granted a role at this location. |
| `data.created[].members[].userId` | string |  |
| `data.created[].members[].userName` | string |  |
| `data.created[].members[].roleId` | string |  |
| `data.created[].members[].roleName` | string |  |
| `data.created[].members[].roleColor` | string \| null |  |
| `data.created[].members[].source` | string \| null | `sso:<connectionId>` when an identity provider granted this; null when granted in Brix. |
| `data.created[].featureOverrides` | any | Owner-set feature overrides, as stored (normally an array). |
| `data.created[].approval` | ApprovalPolicy | The location's content-approval policy, as stored. New locations inherit `{required:false, inheritFromParent:true, …}`. |
| `data.created[].approval.required` | boolean |  |
| `data.created[].approval.inheritFromParent` | boolean |  |
| `data.created[].approval.escalateUpTiers` | boolean |  |
| `data.created[].approval.allowSharedExemptions` | boolean |  |
| `data.created[].approval.approvers` | array of string | User ids named as approvers. |
| `data.created[].billing` | any | Owner-set billing metadata as stored, or null. |
| `data.created[].prefs` | LocationPrefs | Inheritable per-location settings (nearest ancestor wins). |
| `data.created[].prefs.location` | object \| null | Physical place screens here inherit (drives weather and other location-aware apps). |
| `data.created[].prefs.language` | string \| null | BCP 47 tag screens here inherit, e.g. `de-DE`. |
| `data.created[].prefs.defaultContent` | object \| null | What screens here play when they have no content of their own. |
| `data.skipped` | array of string | Names skipped: empty, repeated, or already a location there (or a Location ID already in use). |
| `data.locationsSkipped` | array of string | Names created without a town or city, because the one given could not be found. |

Response 401: Missing, expired or revoked bearer token.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 403: You do not hold `org-unit.create` at the parent location.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 422: `parentId` missing or not found, no rows, more than 200 rows, an invalid Location ID or time zone, or a parent that is a separate workspace.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |

Response 5XX: Server error. The body carries a `requestId` to quote to support.

| Field | Type | Description |
| --- | --- | --- |
| `error` | string | Machine-readable code: `unauthorized`, `forbidden`, `not_found`, `validation_error`, `conflict`, `rate_limited`, `internal_error`, … |
| `message` | string | Human-readable explanation. Safe to show an operator. |
| `requestId` | string | Present on 5xx: quote it to support. |
