Store and recall

Updated 2026-07-16

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.

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.

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#

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.