> ## Documentation Index
> Fetch the complete documentation index at: https://docs.myrqen.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# Share a report

> One URL that renders differently for each recipient, filtered on the server.

Sharing applies to [synced](/concepts/cloud-sync) reports. A local-only report is a file —
send the HTML if that is what you want.

## Requirements

* The scan was synced, so a cloud report exists.
* You are the **owner** of that report. Workspace members can read it; only the owner can
  create, change, or revoke a share link.
* The report has not passed its retention expiry.

## Share modes

| Mode                | Who can open it                                                                                               |
| ------------------- | ------------------------------------------------------------------------------------------------------------- |
| `private`           | Nobody. Useful as a disabled state.                                                                           |
| `workspace`         | Members of the report's workspace.                                                                            |
| `account_allowlist` | Only the accounts named in the principal rules.                                                               |
| `unlisted`          | Anyone with the link, not discoverable.                                                                       |
| `anyone_with_link`  | Anyone with the link. Anonymous access is allowed only in this mode, and only when `anonymousAllowed` is set. |

Optional on any mode: a password, and an expiry.

## Per-recipient projections

A share carries a **default policy** plus zero or more **principal rules**. Each rule
names a principal — a user id, a normalized email, a workspace id, or `anonymous` — and
the [projection policy](/concepts/reports#synced-reports-and-projections) that principal
gets.

That is what makes one URL render differently per viewer.

```json theme={null}
{
  "mode": "account_allowlist",
  "defaultPolicy": {
    "version": 1,
    "sections": { "mode": "allow_only", "ids": ["metadata", "summary"] },
    "findings": { "mode": "allow_only", "ids": [] },
    "fields": { "evidence": true, "code": true, "reproduction": true }
  },
  "principals": [
    {
      "principalType": "email",
      "principalValue": "auditor@example.test",
      "label": "External auditor",
      "policy": {
        "version": 1,
        "sections": { "mode": "allow_all", "ids": [] },
        "findings": { "mode": "allow_only", "ids": ["MYR-001", "MYR-002"] },
        "fields": { "evidence": true, "code": true }
      }
    },
    {
      "principalType": "email",
      "principalValue": "operator@example.test",
      "label": "Platform operator",
      "policy": {
        "version": 1,
        "sections": { "mode": "allow_all", "ids": [] },
        "findings": { "mode": "allow_only", "ids": ["MYR-003", "MYR-004"] },
        "fields": {}
      }
    }
  ]
}
```

In the dashboard, the share panel previews exactly what each recipient receives before you
create the link.

## Create it over the API

```bash theme={null}
curl -X POST http://localhost:3000/api/v1/reports/<reportId>/shares \
  -H "content-type: application/json" \
  -b "myrqen_session=<your browser session cookie>" \
  -d @share.json
```

The response returns the slug **exactly once**:

```json theme={null}
{ "shareId": "shr_…", "slug": "…", "url": "http://localhost:3000/s/…" }
```

<Warning>
  Plaintext slugs are not stored — only a hash. `GET /api/v1/reports/{id}/shares` lists
  existing shares and their settings, but it cannot re-list a link you did not save. Copy the
  URL when you create it.
</Warning>

## Read a shared report

```bash theme={null}
# JSON projection
curl http://localhost:3000/api/v1/shares/slug/<slug>/report \
  -H "x-myrqen-share-password: <password>"

# Export in any format
curl http://localhost:3000/api/v1/shares/slug/<slug>/export/json \
  -H "x-myrqen-share-password: <password>"
```

Formats: `html`, `json`, `markdown`, `sarif`. Browsers open `/s/<slug>` on whatever origin your deployment serves.

The export route resolves the **same** projection the page does, so no format is a bypass.

## Change or revoke

```bash theme={null}
# Update — reportId is required, because it is what authorizes the change
curl -X PATCH http://localhost:3000/api/v1/shares/<shareId> \
  -H "content-type: application/json" \
  -b "myrqen_session=<cookie>" \
  -d '{"reportId":"<reportId>","expiresAt":"2026-09-01T00:00:00.000Z"}'

# Revoke
curl -X DELETE "http://localhost:3000/api/v1/shares/<shareId>?reportId=<reportId>" \
  -b "myrqen_session=<cookie>"
```

Revocation takes effect immediately.

Setting `"password": null` clears a password; a non-empty string sets a new one. Setting
`"expiresAt": null` removes the expiry.

## What a viewer cannot do

<AccordionGroup>
  <Accordion title="See content their policy excluded">
    Filtering happens server-side. Excluded content is **absent from the payload**, not
    hidden in it — in the page, the JSON, and every export. You can check that directly:

    ```bash theme={null}
    curl -s -b <cookie> http://localhost:3000/api/v1/shares/slug/<slug>/export/json | grep -c MYR-003
    ```
  </Accordion>

  <Accordion title="Learn anything from a link they may not open">
    A viewer with no permission gets no report metadata at all: `SHARE_NOT_AVAILABLE`
    without disclosing whether the report exists. Signed out on a mode that requires an
    account, they are asked to sign in — again with nothing disclosed.
  </Accordion>

  <Accordion title="Brute-force a share password">
    Password attempts are throttled per link — 10 attempts per 10 minutes. Exceeding that
    returns `RATE_LIMITED` with an explanation, not a lockout of the link's legitimate
    readers.
  </Accordion>
</AccordionGroup>

## Limits

Active share links per report come from the plan: 5 on free, 25 on pro, 100 on team, 1000
on enterprise. Exceeding it returns `FORBIDDEN` naming the limit. Revoked links do not
count.
