Skip to content

menu ​

← All modules

6 endpoints.

GET /api/v1/menu ​

Get Menu

The menu tree visible to the current user on the active site.

200 — Successful Response

json
{
  "items": {
    "properties": {
      "id": {
        "type": "integer",
        "title": "Id"
      },
      "title": {
        "type": "string",
        "title": "Title"
      },
      "url": {
        "anyOf": [
          {
            "type": "string"
          },
          {
            "type": "null"
          }
        ],
        "title": "Url"
      },
      "icon": {
        "anyOf": [
          {
            "type": "string"
          },
          {
            "type": "null"
          }
        ],
        "title": "Icon"
      },
      "app_slug": {
        "type": "string",
        "title": "App Slug"
      },
      "min_role": {
        "anyOf": [
          {
            "type": "string"
          },
          {
            "type": "null"
          }
        ],
        "title": "Min Role"
      },
      "sort_order": {
        "type": "integer",
        "title": "Sort Order"
      },
      "colour": {
        "anyOf": [
          {
            "type": "string"
          },
          {
            "type": "null"
          }
        ],
        "title": "Colour"
      },
      "children": {
        "items": {
          "$ref": "#/components/schemas/MenuItemOut"
        },
        "type": "array",
        "title": "Children"
      }
    },
    "type": "object",
    "required": [
      "id",
      "title",
      "app_slug",
      "sort_order"
    ],
    "title": "MenuItemOut"
  },
  "type": "array",
  "title": "Response Get Menu Api V1 Menu Get"
}

POST /api/v1/menu ​

Create Menu Item

Request body

json
{
  "properties": {
    "parent_id": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Parent Id"
    },
    "title": {
      "type": "string",
      "title": "Title"
    },
    "url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Url"
    },
    "icon": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Icon"
    },
    "app_slug": {
      "type": "string",
      "title": "App Slug",
      "default": "system"
    },
    "min_role": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Role"
    },
    "sort_order": {
      "type": "integer",
      "title": "Sort Order",
      "default": 0
    },
    "active": {
      "type": "boolean",
      "title": "Active",
      "default": true
    }
  },
  "type": "object",
  "required": [
    "title"
  ],
  "title": "MenuItemIn"
}

201 — Successful Response

json
{
  "properties": {
    "id": {
      "type": "integer",
      "title": "Id"
    },
    "title": {
      "type": "string",
      "title": "Title"
    },
    "url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Url"
    },
    "icon": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Icon"
    },
    "app_slug": {
      "type": "string",
      "title": "App Slug"
    },
    "min_role": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Role"
    },
    "sort_order": {
      "type": "integer",
      "title": "Sort Order"
    },
    "colour": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Colour"
    },
    "children": {
      "items": {
        "$ref": "#/components/schemas/MenuItemOut"
      },
      "type": "array",
      "title": "Children"
    }
  },
  "type": "object",
  "required": [
    "id",
    "title",
    "app_slug",
    "sort_order"
  ],
  "title": "MenuItemOut"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

PUT /api/v1/menu/{item_id} ​

Update Menu Item

Parameters

NameInRequiredTypeDescription
item_idpathyesinteger

Request body

json
{
  "properties": {
    "parent_id": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Parent Id"
    },
    "title": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Title"
    },
    "url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Url"
    },
    "icon": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Icon"
    },
    "app_slug": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "App Slug"
    },
    "min_role": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Role"
    },
    "sort_order": {
      "anyOf": [
        {
          "type": "integer"
        },
        {
          "type": "null"
        }
      ],
      "title": "Sort Order"
    },
    "active": {
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "null"
        }
      ],
      "title": "Active"
    }
  },
  "type": "object",
  "title": "MenuItemPatch"
}

200 — Successful Response

json
{
  "properties": {
    "id": {
      "type": "integer",
      "title": "Id"
    },
    "title": {
      "type": "string",
      "title": "Title"
    },
    "url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Url"
    },
    "icon": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Icon"
    },
    "app_slug": {
      "type": "string",
      "title": "App Slug"
    },
    "min_role": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Min Role"
    },
    "sort_order": {
      "type": "integer",
      "title": "Sort Order"
    },
    "colour": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Colour"
    },
    "children": {
      "items": {
        "$ref": "#/components/schemas/MenuItemOut"
      },
      "type": "array",
      "title": "Children"
    }
  },
  "type": "object",
  "required": [
    "id",
    "title",
    "app_slug",
    "sort_order"
  ],
  "title": "MenuItemOut"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

DELETE /api/v1/menu/{item_id} ​

Delete Menu Item

Parameters

NameInRequiredTypeDescription
item_idpathyesinteger

200 — Successful Response

json
{
  "type": "object",
  "additionalProperties": true,
  "title": "Response Delete Menu Item Api V1 Menu  Item Id  Delete"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

POST /api/v1/menu/reorder ​

Reorder Menu

Persist a new top-to-bottom order for a parent's children by rewriting sort_order. Reorders siblings within that parent only — the top-level groups (apps) themselves are never touched here; their order comes from the Apps Library (POST /apps/reorder).

The client sends only the ids it can see (visibility is gated by installed apps + role). We renumber the parent's WHOLE sibling set so those submitted ids lead in the given order, and any sibling the client couldn't see keeps its relative position right after — so reordering a partial view never makes two siblings collide on the same sort_order or silently shuffles a hidden group into the middle of the visible ones.

The menu is one platform-wide structure, so the same order goes everywhere. Until 2026-08-25 this handler walked site_registry.list_sites() itself and wrote each site in turn, which reached the sites on THIS box and nothing else — portal.telecloud.co.za, portal.lubb.co.za and the rest kept whatever order they had. It now replays the edit onto the hub and hands off to app.services.hub_sync, which converges the whole estate, peer boxes included, and retries anything that was unreachable. See that package's docstring for why the hub is written first rather than the sites directly.

Request body

json
{
  "properties": {
    "parent_id": {
      "type": "integer",
      "title": "Parent Id"
    },
    "ordered_ids": {
      "items": {
        "type": "integer"
      },
      "type": "array",
      "title": "Ordered Ids"
    },
    "ordered_app_slugs": {
      "anyOf": [
        {
          "items": {
            "type": "string"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "title": "Ordered App Slugs"
    }
  },
  "type": "object",
  "required": [
    "parent_id"
  ],
  "title": "ReorderIn"
}

200 — Successful Response

json
{
  "additionalProperties": true,
  "type": "object",
  "title": "Response Reorder Menu Api V1 Menu Reorder Post"
}

422 — Validation Error

json
{
  "properties": {
    "detail": {
      "items": {
        "properties": {
          "loc": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            },
            "type": "array",
            "title": "Location"
          },
          "msg": {
            "type": "string",
            "title": "Message"
          },
          "type": {
            "type": "string",
            "title": "Error Type"
          }
        },
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "title": "ValidationError"
      },
      "type": "array",
      "title": "Detail"
    }
  },
  "type": "object",
  "title": "HTTPValidationError"
}

POST /api/v1/menu/reset ​

Reset Menu

Wipe the active site's menu and rebuild the canonical defaults.

200 — Successful Response

json
{
  "additionalProperties": true,
  "type": "object",
  "title": "Response Reset Menu Api V1 Menu Reset Post"
}

Lubb One Documentation