# pasteToPrint Inline Text Deep Link v1

## Status

This document defines the version 1 inline-text deep-link contract. Receiver
support is implemented in `assets/js/external-import.js`. Clients MUST NOT
advertise the link as supported on the production service until that receiver
version is deployed there.

The contract is intended for AI chat clients that can return a clickable link
but cannot call the pasteToPrint API. It transfers short plain-text documents
without an HTTP `POST`, temporary server session, clipboard operation, browser
extension, or account.

## Canonical form

The canonical production link is:

```text
https://www.pastetoprint.com/#text={payload}
```

`payload` is the UTF-8 document text encoded with the percent-encoding produced
by JavaScript `encodeURIComponent`. The producer MUST encode the value exactly
once.

Example source text:

```text
Prvý riadok
Druhý riadok + poznámka
```

Canonical link:

```text
https://www.pastetoprint.com/#text=Prv%C3%BD%20riadok%0ADruh%C3%BD%20riadok%20%2B%20pozn%C3%A1mka
```

The following characters therefore have these canonical representations:

| Source value | Encoded value |
| --- | --- |
| space | `%20` |
| line feed | `%0A` |
| `+` | `%2B` |
| `#` | `%23` |
| `&` | `%26` |
| `=` | `%3D` |
| `%` | `%25` |

Producers SHOULD normalize line endings to line feed (`U+000A`) before
encoding. Receivers MUST preserve the decoded text and MUST NOT interpret it as
HTML, CSS, Markdown, JavaScript, or a URL.

## Receiver behavior

After the pasteToPrint application has loaded, the receiver:

1. reads the fragment locally;
2. verifies that it contains exactly one non-empty `text` value;
3. checks the encoded-size limit before decoding;
4. decodes the value once as percent-encoded UTF-8;
5. rejects malformed encoding, forbidden control characters, empty text, and
   oversized text;
6. immediately removes the consumed fragment with `history.replaceState`;
7. creates the equivalent internal document payload:

   ```json
   {
     "title": "",
     "mode": "replace",
     "items": [
       {
         "kind": "text",
         "text": "decoded payload"
       }
     ]
   }
   ```

8. displays the existing external-import preview;
9. waits for the user to choose append, replace, or cancel.

Opening the link MUST NOT automatically alter the editor, print, export, or
send the document. The receiver MUST treat the payload as plain text and render
it using the same escaping rules as a normal text import.

## Import-selector precedence

For backward compatibility, the receiver uses this order when more than one
import mechanism is present:

1. `?externalImport=1` window-to-window handshake;
2. `agentImport` capability token in the query or fragment;
3. structured inline fragment `#ptdf=`;
4. inline fragment `#text=`;
5. remote JSON `?import=`;
6. legacy query `?text=`.

A higher-priority selector suppresses all lower-priority selectors. A fragment
containing duplicate `text` members is invalid. Unknown fragment members do
not activate an import.

New AI clients SHOULD emit only the canonical fragment form and MUST NOT emit
the legacy `?text=` form.

## Limits

Two profiles are defined:

- Portable recommendation: the complete URL SHOULD NOT exceed 8,000
  characters. This improves compatibility with AI chat renderers, messaging
  applications, and browsers.
- Receiver hard limit: the fragment MUST NOT exceed 65,536 characters and the
  decoded text MUST NOT exceed 32,768 UTF-8 bytes.

The receiver MUST apply both hard limits before opening the preview. Producers
SHOULD use PTDF files, a browser integration, or an API-backed session for
content that does not fit the portable profile.

The decoded value MUST contain at least one non-whitespace character. Horizontal
tab, carriage return, and line feed are allowed. `U+0000`, `U+0001` through
`U+0008`, `U+000B`, `U+000C`, `U+000E` through `U+001F`, and `U+007F` are
forbidden.

## Network and privacy properties

The URL fragment is not included in the HTTP request that loads
`https://www.pastetoprint.com/`. Processing `#text=` MUST NOT perform a
document-content `fetch`, create a server session, or send the decoded content
to pasteToPrint.

The application itself is still loaded using normal HTTPS `GET` requests. This
contract means that the document content is not part of those requests; it does
not mean that pasteToPrint operates without a network connection.

The fragment can still be visible to:

- the AI chat that generated the link;
- the user and anyone with access to the chat history;
- browser history before the receiver removes it;
- installed browser extensions;
- screenshots, screen recording, or copied links.

Producers MUST NOT place passwords, API tokens, private keys, payment-card data,
or other secrets in an inline-text deep link. Client-side analytics and error
reporting MUST NOT record `location.href`, `location.hash`, the encoded payload,
or the decoded document text.

## Compatibility

Version 1 is limited to plain text. Rich content is intentionally excluded.
Structured documents use the separate `ptdf` fragment member specified in
[`INLINE-PTDF-DEEP-LINK-V1.md`](INLINE-PTDF-DEEP-LINK-V1.md) and MUST NOT
reinterpret `text`.

The existing query form `?text=` remains a legacy compatibility input. It is
not equivalent in privacy properties because a query string is included in the
HTTP request and can appear in server and intermediary logs.

Conforming producers generate the canonical link. Conforming receivers enforce
the limits and review flow in this document. Supporting only the legacy query
form does not constitute support for Inline Text Deep Link v1.
