Starting with the release of Strategy ONE (March 2024), dossiers are also known as dashboards.
The script below has been created in Jupyter Notebook with Strategy 2021u.7 and mstrio 11.3.6.101.
Main steps in this script:

Attached to this article are:
Please note - I've added color formatting in the code below (http://hilite.me/). Code copied from here might not work due to indentation issues. In that case, use attached file with sample code.
from mstrio.connection import Connection
import getpass
from mstrio.server import Environment, Project
from mstrio.access_and_security.privilege import Privilege
from mstrio.users_and_groups import (User, UserGroup)
#from mstrio.connection import get_connection
from mstrio.distribution_services import (
EmailSubscription,
list_subscriptions,
Subscription,
SubscriptionManager
)
mstr_username = "mstr"
mstr_password = getpass.getpass(prompt='Password ')
mstr_base_url = "https://env-XXXXXX.customer.cloud.Strategy.com"
mstr_url_api = mstr_base_url+"/MicroStrategyLibrary/api"
conn=Connection(mstr_url_api, mstr_username, mstr_password, login_mode=1)
env = Environment(connection=conn)
# ### List all projects
all_project_ids = [project.id for project in env.list_loaded_projects()]
print(f"Project count: {len(all_project_ids)}")
# ### List all subscriptions
subs_all_list=[]
for project_id in all_project_ids:
print(project_id)
subs=list_subscriptions(connection=conn, project_id=project_id)
subs_all_list.extend([[project_id, s.id, s.name, s.owner] for s in subs])
print(f"\nSubscriptions count (+ one example): {len(subs_all_list)}\n{subs_all_list[:]}")
# ### List all recipients
# create a dict of dicts per recipient type
recipient_types=['contact', 'contact_group', 'personal_address', 'unsupported','user','user_group']
recipient_type_dicts={}
for r in recipient_types:
recipient_type_dicts[r]=dict()
# In[ ]:
# this one contains all recipients subscriptions (many entries for each recipient),
# will be used for removing recipients without DS provileges at the end
user_subscr_list=[]
for i,s in enumerate(subs_all_list): # this one will create a distinct list of recipients
print(i)
sub = Subscription(connection=conn, subscription_id=s[1], project_id=s[0])
for r in sub.recipients:
user_subscr_list.append([r['id'],s[0],s[1]]) #[recipient_id, project_id, subscription_id]
r_id, r_type = r['id'],r['type']
recipient=[r['name'],r['isGroup'],r_type]
if r['isGroup']:
recipient_type_dicts['user_group'][r_id]=recipient
print("\n\n\n#############\n",recipient,"\n#############\n\n\n")
else:
if not recipient_type_dicts[r_type].get(r_id):
recipient_type_dicts[r_type][r_id]=recipient
print("\nNumber of different recipient types (distinct):")
for r in recipient_types:
if recipient_type_dicts[r]:
print("-",r.ljust(20), len(recipient_type_dicts[r]))
print(f"\nNumber of recipients-subscription combinations: {len(user_subscr_list)}\n")
# In[ ]:
recipient_type_dicts
# In[ ]:
# groups should not be recipients, but sometimes they are and then it looks like this:
'''
[{'id': 'CF1185B64B9152EBC5AB1E8CF0340981',
'name': 'All dossier consumers in Library',
'isGroup': True,
'type': 'all_consumers',
'includeType': 'TO',
'childSubscriptionId': '72EEC233491BA46E449C6CB141385256'}]
'''
# see that group name is added to 'type' :(
# ### Define Server Distribution privileges list
distr_privil=[]
df_p=Privilege.list_privileges(conn, to_dataframe=True)
for index, row in df_p.iterrows():
if row['categories'][0]=="Server - Distribution":
distr_privil.append(row['id'])
print(distr_privil)
# ### Get User privileges
users_without_ds=[]
for i,recip in enumerate(recipient_type_dicts['user'].keys()):
print(i)
user = User(connection=conn, id=recip)
if not any(i["privilege"]["id"] in distr_privil for i in user.list_privileges()):
print("##### no DS #####\n")
users_without_ds.append(recip)
print(f"\nNumber of recipients without DS privilege: {len(users_without_ds)}\n")
# ### Remove recipients without DS privileges from subscriptions
# identify subscriptions to clean
# row = [recipient_id, project_id, subscription_id]
for row in user_subscr_list:
if row[0] in users_without_ds:
print(row)
sub = Subscription(connection=conn, subscription_id=row[2], project_id=row[1])
#print(sub.recipients)
sub.remove_recipient(recipients=row[0])
# # Second approach
# With this approach we brute force it. We skip the step 'List all recipients' above
# We check all users and identify those without DS privileges - just as with 1st approach
# Then, we iterate through all subscriptions and just remove users from 'users_without_ds' list
for proj_id in all_project_ids:
sub_mngr = SubscriptionManager(connection=conn, project_id=proj_id)
for s in sub_mngr.list_subscriptions(to_dictionary=False):
for USER_TO_REMOVE_ID in users_without_ds:
if USER_TO_REMOVE_ID in [r['id'] for r in s.recipients]:
print(s)
print(f"### Project: {proj_id}\n### User: {USER_TO_REMOVE_ID}")
#s.remove_recipient(recipients=USER_TO_REMOVE_ID)