Skip to content

Bulk Export and Write-Back

Pisama's detections are most useful when you can pull them into your own notebook, slice them your own way, and push your verdicts back. This guide covers the full loop: export everything as JSONL, analyze locally, write review verdicts back in bulk, and watch your observed precision improve. Your review feedback is how Pisama earns precision on your data.

All endpoints are tenant-scoped under /api/v1/tenants/{tenant_id} and take your API key as a bearer token.

1. Export detections

GET /detections/export streams every matching detection as JSONL (default) or CSV:

curl -s -H "Authorization: Bearer $PISAMA_API_KEY" \
  "https://api.pisama.ai/api/v1/tenants/$TENANT_ID/detections/export?date_from=2026-06-01" \
  > detections.jsonl
  • Same filters as the list endpoint: detection_type, validated, confidence_min/confidence_max, trace_id, date_from/date_to, environment, framework_filter, agent.
  • format=csv gives a spreadsheet-ready file; the details column is a JSON-encoded string.
  • Exports include reviewed false positives by default (include_false_positives=true), because review labels are exactly what local analysis needs.
  • Rows stream in stable id order, so memory stays flat on both ends.
  • Hard cap: 100,000 rows per request. Above the cap the request fails fast with HTTP 413; narrow date_from/date_to and export in slices.

Each row carries the raw fields: id, trace_id, state_id, detection_type, confidence, method, validated, false_positive, review_status, reviewed_at, created_at, details. Traces are joinable via GET /traces/{trace_id}.

2. Analyze locally

import pandas as pd

df = pd.read_json("detections.jsonl", lines=True)

# Which failure modes fire most, and at what confidence?
print(df.groupby("detection_type")["confidence"].describe())

# Candidates for review: high-confidence, not yet reviewed
todo = df[(df.review_status == "pending") & (df.confidence >= 70)]
print(todo[["id", "detection_type", "confidence", "trace_id"]].head(20))

3. Write verdicts back in bulk

POST /review/batch ingests your verdicts atomically:

curl -s -X POST -H "Authorization: Bearer $PISAMA_API_KEY" \
  -H "Content-Type: application/json" \
  "https://api.pisama.ai/api/v1/tenants/$TENANT_ID/review/batch" \
  -d '{
    "reviews": [
      {"detection_id": "<id-1>", "verdict": "confirmed"},
      {"detection_id": "<id-2>", "verdict": "false_positive", "notes": "expected retry, not a loop"}
    ]
  }'

Verdicts are confirmed, false_positive, or disputed. Detections reviewed as false positives drop out of your dashboard totals, feed Pisama's per-tenant detection memory, and inform threshold suggestions for your tenant. Single detections can also be validated inline with POST /detections/{id}/validate.

4. Track progress

curl -s -H "Authorization: Bearer $PISAMA_API_KEY" \
  "https://api.pisama.ai/api/v1/tenants/$TENANT_ID/review/stats"

Returns totals for pending, confirmed, false positives, and disputed, plus the agreement rate between your verdicts and Pisama's detections on your traffic. That observed agreement on your own traces is the number to watch; it is more meaningful for your deployment than any global benchmark.

Round trip in one script

import os
import pandas as pd
import requests

BASE = f"https://api.pisama.ai/api/v1/tenants/{os.environ['TENANT_ID']}"
HDRS = {"Authorization": f"Bearer {os.environ['PISAMA_API_KEY']}"}

rows = requests.get(f"{BASE}/detections/export", headers=HDRS, timeout=300)
df = pd.read_json(rows.text, lines=True)

false_positives = df[(df.detection_type == "loop") & (df.confidence < 50)]
reviews = [
    {"detection_id": rid, "verdict": "false_positive", "notes": "below my loop bar"}
    for rid in false_positives["id"]
]
if reviews:
    r = requests.post(f"{BASE}/review/batch", headers=HDRS, json={"reviews": reviews})
    print(r.json())