Recipe: Export hours

Pull completed sessions and push them into payroll. Two ways to do it: poll the API, or subscribe to session.completed webhooks for push delivery.

Poll completed sessions

Filter by status and since (updated-at watermark), and follow the cursor:

curl "https://api.briefqr.com/v1/sessions?status=completed&since=2026-06-01&limit=100" 
  -H "Authorization: Bearer bqr_live_..."
import os, requests

def export_since(watermark: str):
    base = "https://api.briefqr.com/v1/sessions"
    headers = {"Authorization": f"Bearer {os.environ['BRIEFQR_KEY']}"}
    cursor = None
    while True:
        params = {"status": "completed", "since": watermark, "limit": 100}
        if cursor:
            params["cursor"] = cursor
        page = requests.get(base, headers=headers, params=params).json()
        for s in page["data"]:
            yield s
        if not page["has_more"]:
            break
        cursor = page["next_cursor"]

The fields you need

Each session carries the operator, hours, and timestamps:

{
  "object": "session",
  "id": "…",
  "status": "completed",
  "operator": { "id": "…", "ident": "NURSE01", "name": "Jane Doe", "employee_id": "E-1" },
  "location": { "id": "…", "name": "Ward A" },
  "check_in":  { "time": "2026-06-02T08:00:00Z", "lat": 51.5, "lng": -0.1 },
  "check_out": { "time": "2026-06-02T15:30:00Z", "lat": 51.5, "lng": -0.1 },
  "total_hours": 7.5,
  "break_minutes": 30,
  "is_flagged": false,
  "flag_reason": null,
  "source": "scan"
}

Map operator.employee_id (or ident) to your payroll id, and total_hours to the pay line. Check is_flagged — a flagged session (missing GPS, manual entry, edited) may warrant review before you pay it.

Keep a watermark

Persist the largest check_out.time (or your run timestamp) and pass it as since next run so you only fetch new/updated sessions. Prefer real-time? Subscribe to the session.completed and session.updated webhooks instead.