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

# Public links

> Share a read-only, expiring page and files your Space generated with someone who has no Luno account.

A public link is a small, read-only snapshot your Space publishes for one person outside your organization (a customer, a patient) to open on their phone: an HTML page plus attached files such as a calendar file (`.ics`), a PDF or an image. Anyone with the URL can open it until it expires; nobody needs to sign in.

The platform serves exactly the bytes you uploaded. It never reads your tables, runs your code or fetches anything when the link opens, so a link always shows the data as it was when you published it. When the data changes, publish a new link (and revoke the old one).

## Example requests

> "When a patient books through WhatsApp, send them a link with their upcoming appointments and an Add to calendar button."

> "Email each customer a link to their monthly report PDF that stops working after 30 days."

## Publish a link

Call `publicLinks.create` from a workflow step or an agent tool (worker runs only, not the CLI):

```ts src/workflows/send-appointments.ts theme={null}
import { publicLinks } from "@meetluno/luno/public-links";

const link = await publicLinks.create({
  files: [
    { path: "index.html", contentType: "text/html", content: page },
    { path: "turnos.ics", contentType: "text/calendar", content: ics },
  ],
  expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
});

// link.url       → https://l.apps.meetluno.ai/<org-slug>/<token>/ (send it by WhatsApp, email…)
// link.id        → keep it (e.g. in a table) to revoke the link later
// link.expiresAt → ISO timestamp
```

`content` is a string (stored as UTF-8) or a `Uint8Array` for binary files. The link opens `index.html` (or the first file when there is none); each file is also served at `<url><path>`, so the page links to it with a relative `href="turnos.ics"`.

Revoke a link before it expires with its id. The files are deleted immediately; revoking an id twice is a no-op.

```ts theme={null}
await publicLinks.revoke(link.id);
```

## Limits

| | |
| - | - |
| Content types | `text/html`, `text/calendar`, `application/pdf`, `image/png`, `image/jpeg` |
| Files | Up to 10, flat names (letters, digits, `.`, `_`, `-`), 1 MB in total |
| Expiry | Required, at most 120 days ahead. After it the link answers `410 Gone`; the files are deleted within a day |
| Links | 2000 per Space (expired links count until they are deleted, within a day) |

## What a page can do

Pages are served from their own host with a strict policy:

* **No JavaScript** and nothing loaded from other sites. Use inline `<style>` and images you attach or embed as `data:` URLs.
* Plain `<a href>` links to other sites work (for example a Google Calendar link). Links never send the page URL as a referrer.
* Never indexed by search engines, never cached, never framed by another site.

Because there is no script, a page cannot detect the phone. For an "Add to calendar" page, show both options side by side:

* **iPhone:** link the attached `.ics` (`text/calendar`, one `VEVENT` per appointment). Opened in Safari, iOS offers to add every event at once.
* **Android:** one Google Calendar link per event, `https://calendar.google.com/calendar/render?action=TEMPLATE&text=…&dates=20261117T130000Z/20261117T140000Z&location=…`, with `&recur=RRULE:FREQ=WEEKLY;COUNT=10` for a weekly series.

## Privacy

Whoever holds the URL can open the page, and messages get forwarded. Put in only what that one person may see, choose the shortest useful `expiresAt`, and revoke links you no longer need. Publish a link only for the person or record your code resolved for the run, never for an id the model or the message text supplies (see [Security by default](https://docs.os.meetluno.ai/learn/agents/build-agents.md#security-by-default)).

The URL starts with your organization's slug (set by the platform, so the recipient sees whose link it is) followed by a random 144-bit token. The link opens only under your organization's **current** slug: if you rename the organization, links sent before the rename stop working (404); publish new ones. The platform stores only the token's hash and logs only the link id, Space and status of each request, never the token or the content.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.