Sharing & access control
Every report has one owner, a visibility level, and a link permission. The combination answers the two questions that matter: who can open it and what can they do once it's open. All of it is enforced server-side — the same rules apply whether the report is opened in a browser, fetched over the REST API, or read by an agent through MCP.
Visibility: who can open it
| Level | Who can open the report |
|---|---|
private |
Only the owner and people explicitly invited. |
team |
Members of the report's team. |
domain |
Anyone signed in with an email on your company domain. |
registered |
Any signed-in Comma user with the link. |
public |
Anyone with the link, no account needed. |
Link permission: what the link grants
For reports that are reachable by link, the link itself carries a
permission: view (read only), comment (read + leave anchored comments),
edit (full collaboration), or none (the link alone grants nothing —
access must come from an invite, team membership, or domain match).
Per-person roles
Independent of the link, you can grant individuals a role on a report: owner, admin, editor, commenter, or viewer. Roles win over the link — a teammate invited as editor can edit even when the link is view-only.
Extra gates
- Password-protected links (every plan) — set or clear a password on the share link; the password is required in addition to the link.
- Domain-gated viewing (every plan) —
domainvisibility checks the viewer's verified email domain server-side; no per-viewer invites, no viewer seats to buy. Pairs with SSO/SAML and SCIM for teams on an identity provider.
Setting access from the API or an agent
The share state is readable with reports:read and writable with the
dedicated sharing scope — so a publishing token can't silently widen
access:
curl -X PUT https://commareports.com/api/v1/reports/REPORT_ID/share \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "visibility": "team", "public_permission": "none" }'
domain visibility takes the allowlist in the same call. Pass bare domains;
they're lowercased and de-duplicated. The list is required with domain and
replaces whatever was there, so to add or remove a domain, send the full new
list. Switching to any other visibility clears it.
curl -X PUT https://commareports.com/api/v1/reports/REPORT_ID/share \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "visibility": "domain", "allowed_email_domains": ["acme.com", "acme.io"], "public_permission": "comment" }'
Agents do the same through the MCP tool set_report_sharing, which takes
the same visibility and allowed_email_domains fields.
Staged publishing
Turn on Publish only approved revisions in the share dialog, or send
show_approved_only to the same endpoint, and view-only readers (including
password-link visitors and embeds) see the last approved revision while
reviewers keep seeing the latest draft. Until the first approval, readers see
the latest version. It controls what readers are shown, not what they can
access. See Staged publishing →.
curl -X PUT https://commareports.com/api/v1/reports/REPORT_ID/share \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "show_approved_only": true }'
Embedding on another site
A report can also render inside your own docs, wiki or customer portal — see Embed an HTML report →. Embedding is off until you name the sites allowed to frame the report. That list is set over the same share endpoint, or by an agent; it isn't in the share dialog.
curl -X PUT https://commareports.com/api/v1/reports/REPORT_ID/share \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "allowed_embed_origins": ["https://docs.acme.com"] }'
Entries are bare origins (scheme, host, optional port, no path), up to 20
per report. They must be https, except http://localhost and
http://127.0.0.1 for local development. The list replaces whatever was
there; send [] or null to turn embedding off, which stops every
existing embed on its next load.
The response carries embed_url (/embed/REPORT_ID) and embed_snippet,
the two lines to paste into the host page:
<div data-comma-report="REPORT_ID"></div>
<script src="https://commareports.com/embed.js" async></script>
An anonymous iframe can't sign anyone in, so the embed renders only while
the report is public with a link permission other than none, has no
share password, isn't end-to-end encrypted and isn't archived. Otherwise
it returns a 404, whatever the list says.
Agents set the same allowed_embed_origins field through
set_report_sharing, and get the snippet back in the result.
When the content itself is sensitive
Zero-knowledge encrypted reports encrypt in your browser with a key derived from your passphrase; the server stores only ciphertext. The tradeoff is deliberate: encrypted reports can't be link-shared, scheduled, or sent to team destinations — the server can't read them, so it can't serve them to anyone but you. See Security →.
Defaults worth knowing
- Reports your agent creates are viewable by anyone with the link (not
indexed by search engines) unless it passes
visibility: 'private'; anyone with the link can comment after a free sign-in.teamanddomainare set after creation via the share endpoint, which carries the side data they need. - Viewers are never charged for and never need a paid seat — pricing is per publisher, not per reader. See Pricing →.