# Fetch a secure-link encrypted area

Returns the ciphertext and public decryption parameters for a secure-link encrypted area (email,
messaging, or shareable-link audience). Possession of the link is the credential.

> **Note:** This endpoint is public — possession of the link is the credential. The decryption
key travels in the link and is never sent to Aitronos; if a password was set, it is never sent
either. Your client derives the key from the link (and password, if set) and decrypts
on-device.


Refuses areas that have expired or, for burn-after-read areas, that were already opened.

This GET is a safe, repeatable **peek**. For a burn-after-read area it deliberately
**withholds** the ciphertext and parameters (`blob` and `external` are `null`, `requires_open`
is `true`) so a link preview, prefetch, or scanner can never destroy it. To open (and consume)
a burn-after-read area, send an explicit
[`POST /v1/encrypted-areas/{encbin_id}/public/open`](#open-a-secure-link-encrypted-area) on a
real user action; the ciphertext and `external` parameters are returned together there. A
non-burn area returns everything directly from this GET.

## Path parameters

**`encbin_id`** string required

The encrypted-area id (`encbin_`-prefixed) from the secure link.

## Returns

An encrypted-area fetch view.

**`encbin_id`** string · The area id.
**`audience`** string · The audience the area was sealed for — any of `link`, `email`,
`whatsapp`, `imessage`, `slack`, `teams`, or `user`. (`user` appears here only when the area
could not be sealed to that user's identity and fell back to a secure link; every
identity-sealed area is served by the recipient-bound endpoint instead.) Routing is by
`crypto_mode`, not by `audience` — do not switch on `audience` to decide which parameters block
to read.
**`crypto_mode`** string · `external` — selects which parameters block is populated.
**`format_version`** string · The sealing format version.
**`blob`** object · `ciphertext_b64` (base64 ciphertext) and `content_kind` (`text` or `file`).
`null` on a GET peek of a burn-after-read area (open it with `POST .../public/open`).
**`requires_open`** boolean · `true` on a GET peek of a burn-after-read area, meaning the
ciphertext and parameters were withheld and you must `POST .../public/open` to receive them.
`false` otherwise.
**`recipient_bound`** object · `null` for secure-link areas.
**`external`** object · The public parameters your client feeds to the decrypt step, including
the authenticated-data array and the format version. Present only for secure-link areas; `null`
on a GET peek of a burn-after-read area.
**`expires_at`** string · Expiry timestamp (UTC) or `null`.
**`burn_after_read`** boolean · Whether the area self-destructs on first open.
**`password_required`** boolean · Whether your client must prompt for a password before decrypt.
The password is never sent to Aitronos.

Example

```bash cURL
curl "https://api.aitronos.com/v1/encrypted-areas/encbin_9f8e7d6c5b4a3928170615243342516a/public"
```


```python Python SDK
# pip install aitronos-sdk
#
# The Python SDK does not expose a typed `encrypted_areas` namespace yet, and it
# has no generic request method, so call the endpoint directly. This one is
# public — possession of the link is the credential, so no header is needed.
import httpx

area = httpx.get(
    "https://api.aitronos.com/v1/encrypted-areas/encbin_9f8e7d6c5b4a3928170615243342516a/public"
).json()
# Derive the key from the link fragment (and password, if set), then decrypt on-device.
print(area["blob"]["content_kind"], area["password_required"])
```


```python Python
import requests

resp = requests.get(
    "https://api.aitronos.com/v1/encrypted-areas/encbin_9f8e7d6c5b4a3928170615243342516a/public"
)
area = resp.json()
print(area["audience"], area["password_required"])
```


```javascript JavaScript
const resp = await fetch(
  'https://api.aitronos.com/v1/encrypted-areas/encbin_9f8e7d6c5b4a3928170615243342516a/public'
);
const area = await resp.json();
console.log(area.audience, area.password_required);
```

**Response:**


```json 200 OK
{
  "encbin_id": "encbin_9f8e7d6c5b4a3928170615243342516a",
  "audience": "link",
  "crypto_mode": "external",
  "format_version": "v2",
  "blob": { "ciphertext_b64": "TXVjaC1jaXBoZXJ0ZXh0...==", "content_kind": "text" },
  "requires_open": false,
  "recipient_bound": null,
  "external": {
    "adata": [["b64nonce", "b64salt", 100000, 256, 128, "aes", "gcm", "zlib"], "plaintext", 0, 1],
    "format_version": 2
  },
  "expires_at": "2026-07-24T09:00:00Z",
  "burn_after_read": true,
  "password_required": true
}
```

> This 200 body is a **`POST .../public/open`** response (or a GET of a non-burn area). A GET
**peek** of a burn-after-read area instead returns `"blob": null`, `"external": null`,
`"requires_open": true`.


Errors

```json 404 Not Found
{ "success": false, "error": { "code": "ENCRYPTED_AREA_NOT_FOUND", "message": "Encrypted area not found.", "system_message": "Encrypted area not found", "type": "client_error", "status": 404, "details": { "encbin_id": "encbin_..." }, "trace_id": "...", "timestamp": "..." } }
```


```json 410 Expired
{ "success": false, "error": { "code": "ENCRYPTED_AREA_EXPIRED", "message": "This encrypted area has expired and can no longer be opened.", "system_message": "This encrypted area has expired", "type": "client_error", "status": 410, "details": { "encbin_id": "encbin_..." }, "trace_id": "...", "timestamp": "..." } }
```


```json 410 Already opened
{ "success": false, "error": { "code": "ENCRYPTED_AREA_BURNED", "message": "This encrypted area was already opened and destroyed.", "system_message": "This encrypted area was already opened and destroyed", "type": "client_error", "status": 410, "details": { "encbin_id": "encbin_..." }, "trace_id": "...", "timestamp": "..." } }
```

## Open a secure-link encrypted area

The non-idempotent counterpart of the fetch above — call it only on an explicit user action.
For a burn-after-read area, the first call returns the ciphertext plus the `external`
parameters and destroys the area; a second call returns `410 ENCRYPTED_AREA_BURNED`. The
decryption key still travels only in the link and is never sent to Aitronos.

> **Send `url_key_proof`.** Opening consumes the area, so this call requires proof that you
hold the link's key. It is a one-way hash — the key itself is still never sent, and
Aitronos still cannot decrypt anything. Without it (or with a wrong one) the call returns
`403 ENCRYPTED_AREA_KEY_PROOF_REQUIRED` **and the area is not consumed**, so a burn-after-read
area survives a refused open and can still be opened with the full link.


## Path parameters

**`encbin_id`** string required

The encrypted-area id (`encbin_`-prefixed) from the secure link.

## Body parameters

**`url_key_proof`** string required

Proof that you hold the link's key, as lowercase hex:


```
url_key_proof = SHA-256( "aitronos.encrypted-area.url-key-proof.v1\0" || url_key )
```

`url_key` is the base58 text after the `#` in the secure link, used **verbatim** — no
trimming and no case folding (base58 is case-significant). The domain-separator prefix ends
with a NUL byte and is part of the hashed input; a bare `SHA-256(url_key)` is a different
value and is refused. The hex may be upper- or lowercase. Maximum 128 characters.

This is **not** the decryption key and cannot be used to decrypt: the AES key comes from a
separate derivation over the key and your password. Treat the proof as a bearer credential
anyway — anything that can open a burn-after-read area can destroy it — so send it in the
body (never in a URL or query string, which reach proxy and CDN logs) and never log it.

Areas sealed before this parameter existed have no stored proof and accept an open without
one; those links expire on their normal schedule.

## Returns

The same encrypted-area fetch view as the GET, with `blob` and `external` present and
`requires_open` `false`.

Example

```bash cURL
# url_key is the text after the '#' in the secure link.
URL_KEY='3q2NfaJ8kYb1oQx7ZmTuLpR4WvE6sHdCgA9nUiKrXyBt'
PROOF=$(printf 'aitronos.encrypted-area.url-key-proof.v1\0%s' "$URL_KEY" | shasum -a 256 | cut -d' ' -f1)

curl -X POST "https://api.aitronos.com/v1/encrypted-areas/encbin_9f8e7d6c5b4a3928170615243342516a/public/open" \
  -H "Content-Type: application/json" \
  -d "{\"url_key_proof\": \"$PROOF\"}"
```


```python Python
import hashlib

import requests

url_key = "3q2NfaJ8kYb1oQx7ZmTuLpR4WvE6sHdCgA9nUiKrXyBt"  # the text after the '#'
domain = b"aitronos.encrypted-area.url-key-proof.v1\x00"
proof = hashlib.sha256(domain + url_key.encode("utf-8")).hexdigest()

area = requests.post(
    "https://api.aitronos.com/v1/encrypted-areas/encbin_9f8e7d6c5b4a3928170615243342516a/public/open",
    json={"url_key_proof": proof},
).json()
print(area["blob"]["content_kind"])
```


```javascript JavaScript
const urlKey = location.hash.replace(/^#/, ''); // the text after the '#'
const domain = 'aitronos.encrypted-area.url-key-proof.v1\0';
const bytes = new TextEncoder().encode(domain + urlKey);
const digest = await crypto.subtle.digest('SHA-256', bytes);
const proof = [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, '0')).join('');

const resp = await fetch(
  'https://api.aitronos.com/v1/encrypted-areas/encbin_9f8e7d6c5b4a3928170615243342516a/public/open',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ url_key_proof: proof }),
  }
);
const area = await resp.json();
console.log(area.blob.content_kind);
```

Errors

```json 403 Proof missing or wrong
{ "success": false, "error": { "code": "ENCRYPTED_AREA_KEY_PROOF_REQUIRED", "message": "Proof of possession of the link key is required to open this area", "system_message": "Proof of possession of the link key is required to open this area", "type": "client_error", "status": 403, "details": { "encbin_id": "encbin_..." }, "trace_id": "...", "timestamp": "..." } }
```


```json 410 Already opened
{ "success": false, "error": { "code": "ENCRYPTED_AREA_BURNED", "message": "This encrypted area was already opened and destroyed.", "system_message": "This encrypted area was already opened and destroyed", "type": "client_error", "status": 410, "details": { "encbin_id": "encbin_..." }, "trace_id": "...", "timestamp": "..." } }
```


```json 429 Too many requests
{ "success": false, "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Rate limit exceeded", "system_message": "Rate limit exceeded", "type": "client_error", "status": 429, "details": { "limit": 60, "window_seconds": 60, "retry_after": 42 }, "trace_id": "...", "timestamp": "..." } }
```

## Related resources

- [Create an encrypted area](/docs/api-reference/encrypted-areas/create)
- [Fetch a recipient-bound encrypted area](/docs/api-reference/encrypted-areas/fetch-recipient-bound)
- [Encrypted areas overview](/docs/api-reference/encrypted-areas/introduction)