# Store and recall

> Capture text or files into a library, then find them again by meaning.

Source: https://fryri.com/docs/api/store-and-recall

`POST /v1/capture` adds material to the library; three read endpoints get it back. There are three ways to send a document, and you pick per call by what you include.

## Three ways to capture

**Text push.** Extract text yourself and send only that. The file's bytes stay on your side.

```bash
curl -X POST https://api.fryri.com/v1/capture \
  -H "Authorization: Bearer fryri_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"text": "MASTER SERVICE AGREEMENT...", "title": "Acme MSA 2026", "source_ref": "s3://acme-docs/msa-2026.pdf"}'
```

`title` names the document in the library. `source_ref` is an opaque pointer back to your original (a URL, object key or your own id); it is stored verbatim, returned on every read, and lets you map a hit back to your file.

**Bytes push.** Send the file itself as `content_base64` with `filename` and `content_type`. Fryri stores it, extracts text (including OCR for scans), and files it.

**Your own bucket.** Register your S3 or R2 bucket once and the raw bytes of every file captured with that key are stored in your bucket instead of Fryri's. Fryri still receives each file to extract and index it; the searchable library stays on Fryri. Connect with bucket keys, or on AWS with an IAM role so no long-lived secret leaves your account. See the reference for the bucket endpoints.

Every capture returns a status: `created`, `deduped` (an identical payload already existed, the returned id points at it), or `rejected` (with a reason and whether a retry can help).

## Capture a local folder

There is no folder connector to configure. Your files are on your machine, so a short loop pushes them: walk the folder, send the bytes. `POST /v1/capture/batch` takes up to 100 items per call and paces them server-side, so you never handle a 429 yourself.

```python
import base64, mimetypes, requests
from pathlib import Path

API = "https://api.fryri.com/v1/capture/batch"
HEADERS = {"Authorization": "Bearer fryri_sk_..."}

items = [{
    "content_base64": base64.b64encode(p.read_bytes()).decode(),
    "filename": p.name,
    "content_type": mimetypes.guess_type(p.name)[0] or "application/octet-stream",
    "source_ref": str(p),
} for p in Path("./contracts").rglob("*") if p.is_file()]

for i in range(0, len(items), 100):
    requests.post(API, headers=HEADERS, json={"items": items[i:i+100]}).raise_for_status()
```

Re-running the loop is safe: a file that hasn't changed comes back `deduped` and nothing is written twice, so the same loop doubles as a crude sync. `source_ref` carries each file's local path back on every search hit.

## Getting it back

```bash
curl "https://api.fryri.com/v1/search?q=service+agreement+termination+notice" \
  -H "Authorization: Bearer fryri_sk_..."
```

`GET /v1/search` matches by meaning. `GET /v1/grep` does literal pattern matching when you know the exact string. `POST /v1/chat` answers a question grounded in the library and cites which documents the answer came from.
