dataverse_list_privileges
Lists defined Dataverse privileges, decoding access rights and grantable depths. Look up what a privilege name means or enumerate privileges for a table.
Instructions
List the privileges DEFINED in the environment — the catalogue of what CAN be granted.
This is the reference list, not an assignment. dataverse_get_role_privileges, dataverse_get_team_privileges and dataverse_retrieve_user_privileges answer "who HOLDS what"; this answers "what privileges exist, what access right does each carry, and at which depths can it be granted". Use it to look up the privilege behind a name those tools return ('prvReadAccount'), or to enumerate everything that exists for one table.
ACCESS RIGHTS ARE DECODED BY A HAND-ROLLED MAP, AND THAT IS THE POINT. The accessright column is an integer with NO option set behind it anywhere in Dataverse: the PicklistAttributeMetadata cast 404s, GlobalOptionSetDefinitions for it 404s, and annotation-included FormattedValues return only the integer with thousands separators. So access_right_name comes from a map derived empirically and cross-checked across every privilege in the environment: 0 None · 1 ReadAccess · 2 WriteAccess · 4 AppendAccess · 16 AppendToAccess · 32 CreateAccess · 65536 DeleteAccess · 262144 ShareAccess · 524288 AssignAccess The gaps are real (8 and 16-32768 are unused), so a name is never derived by shifting bits. AN UNRECOGNISED VALUE IS REPORTED RAW: access_right still carries it, access_right_name is ABSENT, and the value is listed under unmapped_access_rights. Nothing is invented — a wrong access-level label is more dangerous than an unlabelled one, the same discipline dataverse_get_team_privileges applies to Depth. accessright 0 marks the non-CRUD privileges (prvActOnBehalfOf... and friends); it is a real value, not "unknown".
depths COLLAPSES THE FOUR canbe* FLAGS into one ordered list, e.g. ["Basic","Local","Deep","Global"] — the depths at which that privilege may be granted, by increasing scope (Basic = the user's own records, Global = org-wide). Only six combinations exist in practice and nearly every privilege allows Global. An EMPTY depths list is unexpected and means the flags could not be read, not that the privilege can be granted nowhere.
total_count COMES FROM AN AGGREGATION, NOT @odata.count. On this collection @odata.count CAPS AT 5,000 and lies — ?$count=true reports 5,000 where the true catalogue is ~7,346 — so the count is taken with $apply=aggregate($count as c), which bypasses the cap. If a trustworthy total cannot be obtained, total_count is OMITTED and message says so; a capped number is never reported as the truth. count is the size of the returned page, has_more says whether anything was trimmed.
TABLE SCOPING GOES THROUGH A JOIN TABLE, NOT THROUGH PRIVILEGE NAMES. Passing table_logical_name queries privilegeobjecttypecodesset, whose objecttypecode column holds the table's LOGICAL NAME STRING. Filtering by name instead — endswith(name,'Account') — is WRONG in general even though it looks right on the tables people test with: endswith(name,'Role') returns 25 privileges spanning FOUR different tables (role, connectionrole, relationshiprole, mspp_webrole). Privileges are also many-to-many with tables (one privilege can map to as many as 14), which a name can never express. That join table is private and undocumented, so if it fails you get a clear error naming it — never a silent fall back to name matching.
AN UNKNOWN TABLE NAME IS AN ERROR, NOT AN EMPTY LIST. objecttypecode is an EntityName column, so Dataverse validates it: an unknown, misspelled or plural logical name answers HTTP 400 [0x80041102] "The entity with a name = '…' with namemapping = 'Logical' was not found in the MetadataCache", naming the offending entity — that message is the reliable signal for a bad table name, and it is what the error surfaces first. Casing is never the cause: table_logical_name is lowercased for you, matching name_startswith's case-insensitivity on both routes. An EMPTY privileges list means the opposite — the table EXISTS and genuinely has no privileges mapped to it, live-confirmed on 'privilege' itself, which returns 0.
source names the route that actually ran ('privileges' or 'privilegeobjecttypecodesset'); the response shape is identical either way. On the join route, name_startswith and access_right are applied client-side, so count/total_count describe the filtered set.
Each entry: name, privilege_id, access_right (raw integer), access_right_name (absent when unknown), depths, can_be_entity_reference, can_be_parent_entity_reference. Bulky and empty columns are deliberately dropped (privilegetype does not exist on this entity at all).
If the response carries no readable collection, nothing is guessed: normalized is false, no counts are reported, and the body comes back under raw_response. An empty privileges list with normalized: true is a real answer; a missing container is not.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |