Manage workspace access control in Fireflies.ai using roles, channels, privacy levels, and the sharing API. Fireflies uses per-seat licensing with workspace roles and channel-based transcript organization.
| Role | Capabilities |
|---|---|
| Admin | Full workspace control, manage members, access all transcripts |
| Member | Record meetings, view own + shared transcripts |
| Guest | View shared transcripts only (may not consume a seat) |
set -euo pipefail
curl -s -X POST https://api.fireflies.ai/graphql \
-H "Authorization: Bearer $FIREFLIES_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ users { name email user_id is_admin num_transcripts } }"}' \
| jq '.data.users[] | {name, email, admin: .is_admin, transcripts: .num_transcripts}'
// Promote or change a user's role
async function setUserRole(userId: string, role: string) {
return firefliesQuery(`
mutation($userId: String!, $role: String!) {
setUserRole(user_id: $userId, role: $role)
}
`, { userId, role });
}
// Usage: setUserRole("user-id-123", "admin")
// List all channels
const channels = await firefliesQuery(`{
channels {
id
title
is_private
created_by
members { user_id email name }
}
}`);
// Move transcripts to a channel
async function assignToChannel(transcriptIds: string[], channelId: string) {
return firefliesQuery(`
mutation($ids: [String!]!, $channelId: String!) {
updateMeetingChannel(transcript_ids: $ids, channel_id: $channelId)
}
`, { ids: transcriptIds, channelId });
}
Organize by department:
// Privacy levels (most restrictive to least)
type PrivacyLevel =
| "owner" // Only meeting organizer
| "participants" // Only meeting participants
| "teammatesandparticipants" // Workspace members + participants
| "teammates" // All workspace members
| "link"; // Anyone with the link
async function setTranscriptPrivacy(transcriptId: string, privacy: PrivacyLevel) {
return firefliesQuery(`
mutation($id: String!, $privacy: String!) {
updateMeetingPrivacy(transcript_id: $id, privacy_level: $privacy)
}
`, { id: transcriptId, privacy });
}
// Default new transcripts to participants-only
await setTranscriptPrivacy("transcript-id", "participants");
// Share a transcript with up to 100 email recipients
async function shareMeeting(transcriptId: string, emails: string[], expiryDays = 30) {
return firefliesQuery(`
mutation($id: String!, $emails: [String!]!, $expiry: Int) {
shareMeeting(transcript_id: $id, emails: $emails, expiry_days: $expiry)
}
`, { id: transcriptId, emails, expiry: expiryDays });
}
// Revoke access
async function revokeAccess(transcriptId: string, email: string) {
return firefliesQuery(`
mutation($id: String!, $email: String!) {
revokeSharedMeetingAccess(transcript_id: $id, email: $email)
}
`, { id: transcriptId, email });
}
// Rate limit: 10 share operations per hour, up to 50 emails each
// List user groups
const groups = await firefliesQuery(`
query($mine: Boolean) {
user_groups(mine: $mine) {
id name
members { user_id email name }
created_at
}
}
`, { mine: false });
// Use groups to manage channel membership in bulk
async function auditTranscriptAccess(transcriptId: string) {
const { transcript } = await firefliesQuery(`
query($id: String!) {
transcript(id: $id) {
id title
privacy
organizer_email
shared_with
channels { id }
workspace_users
}
}
`, { id: transcriptId });
console.log(`Transcript: ${transcript.title}`);
console.log(`Privacy: ${transcript.privacy}`);
console.log(`Owner: ${transcript.organizer_email}`);
console.log(`Shared with: ${transcript.shared_with?.join(", ") || "none"}`);
console.log(`Channels: ${transcript.channels?.map((c: any) => c.id).join(", ") || "none"}`);
console.log(`Workspace users: ${transcript.workspace_users?.join(", ") || "none"}`);
return transcript;
}
privacy_defaults:
internal_meetings: "teammatesandparticipants"
external_meetings: "participants"
leadership_meetings: "owner"
channel_structure:
- name: "Sales"
private: false
auto_assign: "meetings with external participants"
- name: "Engineering"
private: false
auto_assign: "meetings with 'sprint' or 'standup' in title"
- name: "Leadership"
private: true
members: "C-suite + VPs only"
sharing_policy:
max_expiry_days: 30
require_approval: true # For external sharing
| Issue | Cause | Solution |
|---|---|---|
| Cannot set role | Not workspace admin | Use admin API key |
| Channel not found | Invalid channel ID | List channels first |
| Share rate limited | 10/hour limit | Queue share operations |
| Privacy update fails | Transcript not owned | Only organizer can change privacy |
For migration strategies, see fireflies-migration-deep-dive.