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) — domain visibility 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. team and domain are 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 →.