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=csvgives a spreadsheet-ready file; thedetailscolumn 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_toand 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())
Related¶
- CI Regression Gate for the offline, pre-merge half of the loop.
- Detection Reference for what each detector catches.