Looking for the full interactive reference?

Open API Reference →

Recipe

Training analytics

Aggregate training signoffs across multiple schools into a single analytics dashboard. Suitable when you operate a multi-tenant analytics product and your customers want cross-org rollups computed in your warehouse.

Scopes needed

ScopeWhyAlternative if too broad
public.training:readPull training-progress records keyed by student + program.None — required to access /training-progress.
public.signoffs:readList instructor-validated signoffs (filterable by student/program/period) to compose milestone signals alongside training progress.Drop if your dashboard does not surface signoff-derived metrics.
public.pilots:readResolve the student pilot's name + status alongside the progress + signoff records for dashboard rendering.Drop if your dashboard uses opaque pilot IDs only.

Data flow

bash
┌─────────────────┐     ┌───────────────────┐
│ nightly trigger │ ──▶ │ for each consumer │
└─────────────────┘     │  org: list signoff│
                        │  pages until done │
                        └──────────┬────────┘
                                   │
                                   ▼
                       ┌────────────────────────┐
                       │ stage into your DW raw │
                       │ table (signoffs_raw)   │
                       └───────────┬────────────┘
                                   │
                                   ▼
                       ┌────────────────────────┐
                       │ materialised views per │
                       │ program / instructor / │
                       │ student / period       │
                       └───────────┬────────────┘
                                   │
                                   ▼
                       ┌────────────────────────┐
                       │ dashboard reads MVs    │
                       └────────────────────────┘

Implementation

1. Snapshot — list signoffs per consumer org per day:

bash
# Nightly snapshot per consumer org (paginated)
curl "https://api.gaflight.io/api/v1/public/training/signoffs?from=2026-05-27&to=2026-05-28&limit=200" \
  -H "Authorization: Bearer gaf_pk_<token>" \
  -H "X-Org-Id: <consumer-org-uuid>"

2. Stage — write paged results into your data warehouse raw layer:

js
// Pseudo-code — stage every page into raw, then dedupe in SQL.
async function ingestOrg(orgId, key) {
  let cursor = null;
  do {
    const url = "https://api.gaflight.io/api/v1/public/training/signoffs"
      + "?limit=200" + (cursor ? `&cursor=${cursor}` : "");
    const res = await fetch(url, { headers: authHeaders(key, orgId) });
    const body = await res.json();
    await dw.bulkInsert("signoffs_raw", body.data.map(row => ({
      org_id: orgId, ingested_at: new Date(), payload: row,
    })));
    cursor = body.cursor;
  } while (cursor);
}

3. View — materialise the cuts your dashboard reads from:

bash
-- Materialised view: instructor productivity per period
CREATE MATERIALIZED VIEW mv_instructor_productivity AS
SELECT
  org_id,
  payload->>'instructor_id'             AS instructor_id,
  date_trunc('month', (payload->>'occurred_at')::timestamp) AS period,
  count(*)                               AS signoffs,
  count(DISTINCT payload->>'student_id') AS distinct_students
FROM signoffs_raw
GROUP BY 1, 2, 3;

Edge cases

  • Partial-progress signoffs: a single program item may have multiple signoffs at different completion percentages; take the latest.
  • Instructor-amended signoffs: signoffs can be re-issued; re-fetch the same item on the next run and overwrite the staged row.
  • Schema evolution: training_progress field set may add fields in beta; store the raw payload as JSONB and let your MVs cherry-pick.
  • Historical backfill: on first install you may need to walk back months; the API supports from/to over a one-year window.
  • Cross-org join keys: student_id is stable within a consumer org but NOT across orgs; key your warehouse by (org_id, student_id).
  • Soft-deleted students: keep them in your DW for audit; the API returns them with a deleted_at marker.
  • Time zones: signoffs carry occurred_at in UTC; bucket by your dashboard tz only at view time.

Going live

  1. Declare the two scopes above on your developer app with ≥40-char justifications each.
  2. Stand up the warehouse staging path with idempotent upserts BEFORE submitting your listing.
  3. Submit your listing for review (see /developers/review-policy).
  4. Publish under your org and share the install URL with school customer organizations.
  5. Document your retention policy (signoffs feed pilot records that schools may need for years).

Generic template

This recipe is a generic implementation pattern. No real integration partner is named or endorsed. Substitute your own backend, terminology, and customer fit as appropriate.