Permission Catalog

This page is the canonical catalog of Reply Pilot application permissions. The permission code is the string used by backend and Flask authorization checks. Keep this page in sync with public.app_permission, AppPermissionCodes, backend authorization checks, Flask permission gates, and seeded role grants.

Permission List

Code Definition Scope and important behavior
company.view_assigned User can view companies assigned to them. Assigned company means party_organization.default_email_sales_user_id = app_user.id. Companies without an assigned default email sales user are not visible through this permission alone. Backend read-model company and exact-task reads also include the company linked to any active Jira task assigned to the current local user or Jira-account profile. This task-derived exception is read-only; company collaboration remains limited to active assigned Reply Pilot Task items. Normalized Done tasks never delegate access.
company.view_all User can discover all companies. Sales users receive only a summary for unrelated companies. Bypasses assigned-company discovery filters. For sales, full detail and task access remain subject to the existing relationship scope; for other roles it includes task search.
company.write User can perform global company write operations. Also allows own Pippa scouting conversations and shared candidate decisions with reasons. Accepted for creating companies (Pippa can select another owner with global full-detail access), creating company-linked Reply Pilot Task issues, and existing-company and linked-task mutations, where backend still checks company visibility.
company.write_assigned User can create self-assigned companies and write existing companies and linked tasks within their company visibility scope. Also allows own Pippa scouting conversations and shared public candidate decisions with reasons. New companies default to the current user as default_email_sales_user_id; Pippa onboarding permits another owner only with global full-detail access. For sales users (including those with company.view_all) and other users without company.view_all, existing writes remain limited to companies where the user is the default email communication owner; this also covers creating company-linked Reply Pilot Task issues for visible assigned companies.
company.ai_assist Legacy compatibility permission; no longer used as an access gate. Existing seeded assignments remain. Full company-profile access grants the company scope for Pippa; a valid personal Pippa token and mcp.access are additionally required. Assigning this legacy permission alone grants nothing.
mcp.access User can manage their own personal MCP tokens and authenticate MCP clients. Required for both Pippa and other tokens, including automatic Pippa-token creation on page entry. Every request also checks current company/task permissions. Does not grant company visibility or writes. Token management is blocked during impersonation.
company.merge User can merge duplicate companies. Merge endpoints still require access to the affected company records.
email.inbox.view User can view the dedicated Inbox screen. Required by /inbox and its inbox-specific backend list, detail, and attachment endpoints. It does not restrict /emails, replies, or forwarding.
email.inbox.review User can mark an unresolved email thread as manually reviewed. Required together with email.inbox.view for the durable per-thread review action in the unassigned-email queue. It does not grant Inbox read access or permission to mutate companies, people, or contacts by itself.
security.manage_roles User can manage role assignments. Required for listing all roles, reading another user's assigned role IDs, and replacing user roles. Backend prevents removing the last role manager.
security.impersonate_user User can start a controlled impersonation session for another user. Kept separate from admin. Cannot impersonate self, users with security.manage_roles, or users with security.impersonate_user. Starts are recorded in app_user_impersonation_audit.
task.email_thread_reply.unlinked User can view and edit an Email Thread Reply task whose thread currently resolves to no company. Does not grant company access. If canonical thread resolution later adds a company, normal company visibility and write rules apply immediately. Seeded only to sales_int.
task.supplier_onboarding.ask_question User can ask a merchant a question from Supplier Onboarding. Required by the backend mutation that adds a comment, assigns the task to the selected default merchant, and moves a Rollout or In Development task to Waiting for Info. The task must still be editable by the current user.
user.manage_skills User can manage application-user skill assignments. Required for reading another user's skills and replacing user skill assignments. The authenticated skill catalog and the current user's own assignments remain readable for profile display. Skills control business-work eligibility and do not grant application access.

Delegated task read exception: backend read-model company endpoints may also expose the company linked to any active Jira task when the current app user is its local assignee or the cached Jira assignee matches the user's Jira profile. This includes the company's notes and activities. Exact-task reads use the same active-assignee rule for all Jira work types. The assignee may also trigger POST /api/tasks/{taskId}/refresh-from-jira as read-side cache synchronization; the backend always uses the Jira key stored on that task and ignores any client supplied key. Delegation does not expose unrelated companies or unrelated tasks at the same company and excludes unassigned or normalized Done tasks.

Delegated company collaboration remains narrower than delegated company read: only an active assigned Reply Pilot Task allows its assignee to add company notes, create/update/remove company contact methods, create a new person linked to the company, and remove a person-company relationship. A related person's profile, notes, and contacts may be edited only when the user can collaborate with every active, unmerged company linked through CONTACT_FOR; a standalone person requires global company.write. Delegation does not allow company fields, visibility, identifiers, requirements, ownership, merge, task creation, task-company relinking, or email-thread relinking.

Delegated task mutation exception: assignment to any active Jira task permits the existing operations for that exact task type, including Supplier Onboarding edit/reply, Email Reply edit/status/reassignment/send/forward, and Reply Pilot resolve/reassignment. Reply Pilot close (Dokončit) is available to every authenticated user who can read that exact task, including users without company write permission. It does not require being the requester or assignee, nor a stored requester. The close endpoint enforces the same task read scope as the detail; it does not grant access to otherwise hidden tasks. Jira keys and work types are loaded and validated server-side; legacy client jira_key fields are ignored. An Email Reply task has no independent company link and cannot be relinked; its companies always follow canonical thread resolution. Reassignment or a transition to normalized Done immediately removes assignment-derived access. The unlinked Email Reply permission and Supplier Onboarding question permission remain separate grants.

An authorized Email Thread Reply transition from Reply Pilot to Waiting for Reply or Done also attempts to mark the linked conversation read in every connected mailbox containing the same RFC Message-IDs. Personal mailboxes must have sync_inbox enabled; this side effect does not grant the acting user read access to another mailbox or expose its contents. Gmail modification still requires that mailbox's Google OAuth grant with gmail.modify. Direct Jira changes and cache refreshes do not trigger this operation.

Email replies (RP-8805) use the selected original incoming message's TO/CC/BCC recipients to choose the sender. If velkoobchody@internet-handel.cz appears, only that shared sender is allowed. Otherwise, an imported personal message may be answered only by its source mailbox owner, with sync_inbox enabled, a valid Google send grant, and edit access to the linked task. Company or task access does not grant permission to send from another user's mailbox. The backend checks these conditions again for both drafts and sends; changing outgoing recipients or submitted sender/account fields cannot change mailbox identity. Shared replies retain company visibility and linked-task edit checks. New emails and forwards remain shared-only.

New emails from the composer are allowed for sales only when every recipient resolves to a company the current user can write. The backend resolves each address from stored company/person contacts or registered company domains and checks current company-write scope before calling Gmail. Global visibility, a delegated read/task assignment, or a submitted company/task ID does not bypass that check. Unknown recipients and recipients belonging to an inaccessible company are rejected before delivery. The existing non-sales sending behavior is unchanged.

After sending, sales may create the Email Thread Reply task only for an imported shared Gmail thread with a canonical link to a writable company. This reuses the existing email-task company scope; personal-mailbox threads and threads without a writable company do not qualify. Authorization is checked again before creating or returning an existing task. If task creation fails after delivery, the composer explicitly reports that the email was sent and must not be resent. No new permission or seeded role grant is introduced.

Seeded System Roles

Role Permissions Notes
admin company.view_assigned, company.view_all, company.write, company.write_assigned, company.merge, email.inbox.view, email.inbox.review, security.manage_roles, task.supplier_onboarding.ask_question, user.manage_skills, mcp.access Normal administration of company-linked records. Unlinked Email Thread Reply tasks are reserved for sales_int; security.impersonate_user is also intentionally not included.
sales company.view_assigned, company.view_all, company.write_assigned, company.ai_assist, mcp.access Can discover all available companies and read their basic Summary. Full detail requires ownership or an active assigned task. Company writes remain limited to assigned companies; existing task-derived collaboration rules apply.
sales_int (Obchodník int) company.view_assigned, company.view_all, company.write, company.write_assigned, company.merge, email.inbox.view, email.inbox.review, task.email_thread_reply.unlinked, company.ai_assist, mcp.access Can view and mutate all companies, merge companies, open Inbox, close unresolved threads as reviewed, and handle Email Thread Reply tasks whose thread resolves to no company. Existing sales_manager assignments are migrated to this role.
loader (Zavaděč) company.view_all, company.write, task.supplier_onboarding.ask_question, mcp.access Can view and mutate all companies and ask the merchant a question from an assigned Supplier Onboarding task. Cannot merge companies or manage roles.
support_impersonator security.impersonate_user, user.manage_skills Dedicated support role for impersonation and user-skill administration.

Sales company access (RP-5471)

Migration 0088 grants company.view_all to sales. An active sales role applies the following limits even when the user also holds other roles:

  • Search and the company directory can find all available companies. Unrelated companies expose only the existing basic Summary, including CME contacts. Private notes, local contacts, relationships, requirements, email history, tasks and attachments are not exposed through global company visibility.
  • Full company detail requires the user to be the assigned salesperson (default_email_sales_user_id) or the assignee of an active task linked to that company. This reuses the existing local/Jira-account assignment and normalized Done exclusion, including canonical email-task company links.
  • company.view_all does not expand sales write rights. Assigned-company writes, exact-task edits and task-derived collaboration retain their existing checks. An active generic Reply Pilot Task allows company collaboration; other active assigned task types grant company read access only.
  • Tasks of all types remain subject to the existing task scope: owning the company or assignment to that exact active task. Assignment to one task does not expose other tasks belonging to somebody else's company.
  • Direct activity, email-thread and attachment reads require a related company. Global inbox, unscoped email operations and the meeting wizard remain blocked. New-email sends and follow-up reply-task creation have the company-scoped exceptions described above; outbound Gmail drafts and unscoped forwarding remain blocked. Sales login still opens My Tasks.
  • Search receives sales_company_access: companies use global visibility, while tasks use assigned scope before pagination. The backend rechecks each result and removes private company snippets for unrelated companies. Release backend and search together before applying the role grant.

Other roles retain their existing behavior. The sales limitations above take precedence over the general permission descriptions in this catalog.

Maintenance Rules

  • When adding, renaming, deleting, or changing the meaning of a permission, update this page in the same change.
  • When changing seeded role grants, update the role table above in the same change.
  • When changing authorization behavior without changing the database grant, update the scope and important behavior column.
  • Keep permission labels and descriptions aligned with Liquibase seed data.

Personal MCP tokens

Migration 0092 grants mcp.access to the four normal application roles above. Users can manage only their own credentials through Profile → Tokens, including reveal, copy and revoke. Neither administrators nor impersonation sessions receive access to another user's token originals. Removing mcp.access immediately blocks subsequent MCP requests and token management; it does not cancel a running call. Token type (pippa/other) changes selection by Pippa, not authorization. All MCP tools resolve the actor from the bearer hash and reload existing resource permissions. The old actor-delegation headers are no longer accepted. See MCP.

Pippa company conversations (RP-8820)

Full access to a company profile defines the company scope for Pippa: conversation lists, history, summaries, context, questions and retries, including all company-related notes, activities, imported email threads, tasks and documents. No separate company.ai_assist grant or per-source application permission check is required. This includes full company access derived from an assigned task. Company Summary access alone is insufficient. Opening Pippa, reading ordinary chat history, submitting questions and retries also require mcp.access and an active, decryptable personal Pippa token. This applies to company, onboarding and scouting screens. During impersonation the token must belong to the effective user; the real administrator's token cannot substitute for it. Admin history retains its separate admin gate. On page entry, an authorized user without an active Pippa token gets one automatically (name Pippa, no expiration), including during impersonation. This exception does not expose the effective user's credentials to the impersonator; manual management stays blocked. Concurrent opens reuse one token. Polling and background work only validate tokens. Revoked/expired tokens stay invalid; reopening Pippa can create a replacement, so remove mcp.access to prevent further use. The backend checks company access on every request and before and after background generation; conversation and question IDs remain scoped to their company and conversation.

Stored history inherits the conversation's company access, including saved source snapshots; it is not hidden when an individual source is removed or its separate permissions change. New context is assembled only from the selected company's related records. AI source IDs are resolved only within that server-built manifest. Lists do not load turns, inspect source records, or contact Jira/Google. Live file retrieval still uses the existing Google/Jira integrations, with the acting user's Jira OAuth grant and task/attachment identity validation. Missing provider access makes a live file unavailable; it does not hide saved history.

This rule is specific to Pippa. Direct source screens retain their existing permissions. Pippa chat read access alone grants no mailbox send rights. Its company/contact write tools require the existing company collaboration permission in addition to full profile access, exactly like the corresponding forms. A read grant alone does not enable writes; no new permission or role is introduced. list_assignment_users requires company-write permission. reassign_company requires full company access and current company-write scope, like the existing reassignment form; task-derived contact collaboration alone is insufficient. It changes the default email sales user and all open company tickets, preserving meeting-assignee skill checks and closed tickets. Jira writes use the authenticated actor, not the new assignee. In Pippa onboarding, selecting another default email sales user additionally requires global full-detail company access (company.view_all without the sales-role summary restriction). Users limited to assigned companies continue to create for themselves. No permission codes or role grants are added. add_company_note requires the same company collaboration rights and full company access as the note form. It records the authenticated token owner as author; neither a model nor an external MCP client can supply a different author. It creates only a Reply Pilot company note and needs no Jira OAuth grant. The same tool is available after company creation in onboarding, with that conversation's existing author and company-write gates. Scouting cannot create company notes. send_email requires full company-profile access plus current company-write scope (company.write or applicable company.write_assigned), even when retrieving a stored send result. Every recipient must resolve to that company. An existing communication ticket must belong to the company, be editable by the actor, and include the selected source thread. Company collaboration delegated by a task alone does not grant sending. Only the fixed shared mailbox is used; no personal mailbox/OAuth grant is exposed. The actor's profile signature is used. For a new communication ticket, the actor's valid Jira OAuth account is both creator and explicit Reporter. A missing/invalid grant or inactive Jira account allows a narrowly scoped fallback to the configured application Jira account for creation, description update and the Waiting for Reply transition. The backend validates the chosen identity before Gmail and logs fallback at WARN with the requesting user ID, created Jira key/ID and reason. Permission errors or provider outages do not enable fallback. Existing tickets keep their Reporter and require the actor's grant; Jira attachments and live onboarding checks retain their delegated-access requirements. Application company/task permissions are always checked as the requesting user. A clear user send request authorizes the email and its communication-ticket update, never a mere draft request. Attachment references are restricted to the current company manifest and the existing Google/Jira retrieval checks. list_email_attachments requires full company read access only. See MCP email for retry and result rules. The close_company_opportunity tool instead requires current company-write scope (company.write or applicable company.write_assigned) and task access for the company's tickets. Task-derived contact collaboration alone does not grant closure. get_company_closure requires full company-profile and scoped task-read access. Live Jira reads, result updates, comments and transitions use the acting user's Jira OAuth grant; no service credential bypasses the user's provider permissions. Migration 0089's company.ai_assist permission and seeded role assignments remain for compatibility but no longer control Pippa access.

The personal /pippa/conversations page and GET /api/pippa/conversations require authentication and list only conversations created by the acting user (including when impersonating). The shared mcp.access and personal Pippa token gate applies. Existing full company-read access still applies to company-linked conversations; standalone onboarding and scouting require company.write or company.write_assigned. The personal list does not grant administrator access or expose another author's conversations.

The dedicated /pippa/companies onboarding workflow uses company.write or company.write_assigned to start a company (self-assigned by default; selecting another owner requires the global full-detail access described above). Before creation only the conversation's author can access it. After creation full company access grants read access; follow-up questions, draft changes and approvals additionally require the original author and current company-write scope. No new permission or role is seeded. Duplicate discovery exposes profile details only with full company access; a hidden/summary-only duplicate reveals no record identifiers.

Explicit onboarding approval includes creating the Supplier Onboarding ticket through the author's delegated Jira grant. A separate email approval permits one first-contact email through the existing shared sender and its automatic communication-ticket workflow. This company-scoped action requires the recipient to resolve to the newly created company and does not grant general mailbox or unscoped compose access. Before company creation AI research has no mutation tools. After creation the author can explicitly request opportunity closure or add company notes or contacts, create linked people and update their company-specific roles through the same scoped tools as ordinary company chat. These note/contact tools retain company collaboration checks; the onboarding conversation additionally retains its author and company-write gates. The tools remain available after email completion. Approving the initial company form already includes saving its selected contacts; no separate chat request or new permission is needed for that step. A closed onboarding ticket also blocks approved initial email sending. The backend stores the send claim before the external call; uncertainty blocks resend. Onboarding audit conversations cannot be deleted through ordinary Pippa deletion.

Deleting a Pippa conversation requires the active admin role (Administrator) and full access to its company. This is a role check, not a grant implied by company write access, role-management permission, or the conversation's author. The list exposes can_delete for that user; DELETE /api/companies/{partyId}/pippa/{conversationId} checks the role again and scopes deletion to the company. Deletion permanently removes the conversation and all its turns, saved answers and source snapshots.

The separate /admin/pippa page and /api/admin/pippa API grant an active admin global access to list, read and individually delete all Pippa history, including another author's standalone onboarding conversations. Company scope and author-only workflow rules do not restrict this administrative history view; they still apply to ordinary screens and workflow actions. It provides no question/retry/send/approval actions. The role is checked on every backend request and for the frontend link and routes, using the effective user during impersonation. security.manage_roles or company write permissions alone do not grant access. No new permission or role is seeded. Deletion uses CSRF protection and a confirmation dialog and is rejected while a question or onboarding side effect is in progress. Company, Jira and email records are retained when their conversation history is deleted.

Pippa scouting

/pippa/scouting and /api/pippa/scouting use company.write or company.write_assigned. These grants now also allow public-source candidate research and shared candidate decisions. No new permission or seeded role is added. The author alone can read, continue, retry and change a scouting conversation; admin history access remains a separate read/delete path. Every tool reloads current grants, and generation checks access before and after the provider call.

Candidate data is shared public research. An actor may act on a candidate only through their own scouting conversation; permanent rejection/reopening is shared across searches and authors and requires a reason. Search-only skip/restore does not alter global eligibility. Candidate onboarding requires the same author and company-write rules as ordinary onboarding. A second author cannot take over an existing standalone onboarding conversation. Company search retains existing visibility filtering; exact hidden-company matches reveal only a blocking notice. Admin deletion removes chat history and search membership, retaining canonical candidates, permanent decisions and company associations.