{"base":"window.FLAMINGO_API_BASE || '/api'","errorEnvelope":{"type":"object","properties":{"error":{"type":"string","description":"Stable error code, e.g. <vendor>_not_connected or unauthorized."},"hint":{"type":"string","description":"Optional next step."}},"required":["error"]},"endpoints":[{"path":"/me","method":"GET","description":"The signed-in user (email, groups, permissions).","successSchema":{"type":"object","properties":{"email":{"type":"string"},"groups":{"type":"array","items":{"type":"string"}},"permissions":{"type":"array","items":{"type":"string"}}},"required":["email"]},"errors":[{"status":401,"error":"unauthorized"}]},{"path":"/me/favourites","method":"GET","description":"The signed-in user's pinned sidebar pages, in render order.","successSchema":{"type":"object","properties":{"favourites":{"type":"array","items":{"type":"object","properties":{"href":{"type":"string"},"label":{"type":"string"}},"required":["href","label"]}}},"required":["favourites"]},"errors":[{"status":401,"error":"unauthorized"}]},{"path":"/connections/{slug}/check","method":"GET","description":"Run one connector's read-only connection probe as the calling user. `ok` is the field to branch on; `details` are the facts the live call returned. Slugs come from the connection list — core names no vendor.","successSchema":{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"},"status":{"type":"string","enum":["ok","not-configured","not-connected","failed"]},"ok":{"type":"boolean"},"summary":{"type":"string"},"probe":{"type":"string"},"details":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}},"required":["label","value"]}},"durationMs":{"type":"number"}},"required":["slug","title","status","ok","summary","probe","details","durationMs"]},"errors":[{"status":401,"error":"unauthorized"},{"status":404,"error":"no_connection_check"}]},{"path":"/me/home","method":"GET","description":"Where `/` sends the signed-in user. `path` is null when they use the built-in landing page.","successSchema":{"type":"object","properties":{"path":{"type":["string","null"]}},"required":["path"]},"errors":[{"status":401,"error":"unauthorized"}]},{"path":"/crm/:instance/companies","method":"GET","description":"Companies in a CRM instance, cursor-paginated (?limit, ?cursor). `:instance` is `customers` (external customers, monetary) or `users` (internal staff, admin-only). Use `parentCompanyId` to build a hierarchy — for Minga that is schools under their district — and `arr` for revenue on monetary instances.","successSchema":{"type":"object","properties":{"companies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"domain":{"type":"string"},"industry":{"type":"string"},"employees":{"type":"number"},"parentCompanyId":{"type":"string","nullable":true,"description":"The parent company, when this record is a child of another (e.g. a school under a district). Null at the top of the hierarchy."},"arr":{"type":"number","nullable":true,"description":"Annual recurring revenue. Null when the instance is non-monetary or unset."},"updatedAt":{"type":"number","description":"Epoch ms of the last sync of this record."}},"required":["id","name"]}},"nextCursor":{"type":"string","nullable":true,"description":"Pass back as ?cursor= for the next page; null on the last page."}},"required":["companies"]},"errors":[{"status":401,"error":"unauthorized"},{"status":403,"error":"forbidden"},{"status":404,"error":"no_such_instance"}]},{"path":"/crm/:instance/contacts","method":"GET","description":"Contacts in a CRM instance, cursor-paginated (?limit, ?cursor). `companyId` joins to /crm/:instance/companies.","successSchema":{"type":"object","properties":{"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"companyId":{"type":"string","nullable":true},"lifecycleStage":{"type":"string"},"updatedAt":{"type":"number"}},"required":["id"]}},"nextCursor":{"type":"string","nullable":true,"description":"Pass back as ?cursor= for the next page; null on the last page."}},"required":["contacts"]},"errors":[{"status":401,"error":"unauthorized"},{"status":403,"error":"forbidden"},{"status":404,"error":"no_such_instance"}]},{"path":"/crm/:instance/deals","method":"GET","description":"Deals in a CRM instance, cursor-paginated. Present only on MONETARY instances (`customers`); a non-monetary instance returns an empty list rather than pretending.","successSchema":{"type":"object","properties":{"deals":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"companyId":{"type":"string","nullable":true},"amount":{"type":"number"},"stage":{"type":"string"},"closeDate":{"type":"string"},"updatedAt":{"type":"number"}},"required":["id","name"]}},"nextCursor":{"type":"string","nullable":true,"description":"Pass back as ?cursor= for the next page; null on the last page."}},"required":["deals"]},"errors":[{"status":401,"error":"unauthorized"},{"status":403,"error":"forbidden"},{"status":404,"error":"no_such_instance"}]},{"path":"/crm/:instance/summary","method":"GET","description":"Rollup for a CRM instance: entity totals, contacts by lifecycle stage, and — on monetary instances only — the deal pipeline by stage. `pipeline` and per-stage `amount` are OMITTED for non-monetary instances, so the response stays honest about what that instance tracks; deal COUNTS are always present.","successSchema":{"type":"object","properties":{"totals":{"type":"object","properties":{"contacts":{"type":"number"},"companies":{"type":"number"},"deals":{"type":"number"}}},"lifecycle":{"type":"array","items":{"type":"object","properties":{"stage":{"type":"string"},"count":{"type":"number"}}}},"pipeline":{"type":"array","description":"Monetary instances only; absent otherwise.","items":{"type":"object","properties":{"stage":{"type":"string"},"count":{"type":"number"},"amount":{"type":"number"}}}}},"required":["totals"]},"errors":[{"status":401,"error":"unauthorized"},{"status":403,"error":"forbidden"},{"status":404,"error":"no_such_instance"}]},{"path":"/crm/:instance/sources","method":"GET","description":"Each connector's sync state for this instance — last run, watermark, row counts. Use it to tell a viewer how fresh the numbers are instead of implying they are live.","successSchema":{"type":"object","properties":{"sources":{"type":"array","items":{"type":"object"}}},"required":["sources"]},"errors":[{"status":401,"error":"unauthorized"},{"status":403,"error":"forbidden"},{"status":404,"error":"no_such_instance"}]},{"path":"/vendors/linear/issues","method":"GET","description":"The signed-in user's assigned Linear issues. Add ?project=<id-or-name> to instead get every issue in that project, enriched with priority, priorityLabel, assignee, and project — the shape a project board needs (these fields are absent on the plain assigned list).","successSchema":{"type":"object","properties":{"issues":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"identifier":{"type":"string","description":"e.g. FLA-5"},"title":{"type":"string"},"url":{"type":"string"},"state":{"type":"string","description":"Optional workflow state name."},"priority":{"type":"integer","description":"0 none, 1 urgent, 2 high, 3 medium, 4 low. Project view only."},"priorityLabel":{"type":"string","description":"Human priority label, e.g. \"High\". Project view only."},"assignee":{"type":"string","description":"Assignee display name. Project view only."},"project":{"type":"object","description":"Owning project. Project view only.","properties":{"id":{"type":"string"},"name":{"type":"string"}}}},"required":["identifier","title","url"]}}},"required":["issues"]},"errors":[{"status":401,"error":"unauthorized"},{"status":401,"error":"linear_not_connected"}]},{"path":"/vendors/github/repos","method":"GET","description":"The signed-in user's GitHub repositories (most recently pushed first).","successSchema":{"type":"object","properties":{"repos":{"type":"array","items":{"type":"object","properties":{"nameWithOwner":{"type":"string","description":"e.g. ChangeGamers/flamingo."},"url":{"type":"string"},"isPrivate":{"type":"boolean"}},"required":["nameWithOwner","url","isPrivate"]}}},"required":["repos"]},"errors":[{"status":401,"error":"unauthorized"},{"status":401,"error":"github_not_connected"}]},{"path":"/vendors/gong/calls","method":"GET","description":"Recent Gong calls (trailing 30 days), read via the shared donor credential. The viewer must have opted in at /connections; Gong is not per-user.","successSchema":{"type":"object","properties":{"calls":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"started":{"type":"string","description":"ISO 8601 start time."},"url":{"type":"string"}},"required":["id","title","started","url"]}}},"required":["calls"]},"errors":[{"status":401,"error":"unauthorized"},{"status":403,"error":"gong_not_opted_in"},{"status":409,"error":"gong_donor_not_connected"},{"status":503,"error":"gong_not_configured"}]},{"path":"/vendors/gong/calls/detailed","method":"GET","description":"Recent Gong calls enriched with each call's participants (the \"who's\"). Same donor-credential + opt-in gating as /vendors/gong/calls; one request powers a filter-by-participant.","successSchema":{"type":"object","properties":{"calls":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"started":{"type":"string","description":"ISO 8601 start time."},"url":{"type":"string"},"participants":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"affiliation":{"type":"string","description":"Internal / External / Unknown."},"speakerId":{"type":["string","null"],"description":"Joins to a transcript segment speakerId."}},"required":["name","affiliation","speakerId"]}}},"required":["id","title","started","url","participants"]}}},"required":["calls"]},"errors":[{"status":401,"error":"unauthorized"},{"status":403,"error":"gong_not_opted_in"},{"status":409,"error":"gong_donor_not_connected"},{"status":503,"error":"gong_not_configured"}]},{"path":"/vendors/gong/calls/:id/transcript","method":"GET","description":"One call's transcript as speaker turns, read on demand via the donor credential. Each segment's speakerId joins to a participant from /vendors/gong/calls/detailed.","successSchema":{"type":"object","properties":{"callId":{"type":"string"},"segments":{"type":"array","items":{"type":"object","properties":{"speakerId":{"type":["string","null"]},"text":{"type":"string"}},"required":["speakerId","text"]}}},"required":["callId","segments"]},"errors":[{"status":401,"error":"unauthorized"},{"status":403,"error":"gong_not_opted_in"},{"status":409,"error":"gong_donor_not_connected"},{"status":503,"error":"gong_not_configured"}]},{"path":"/vendors/intercom/conversations","method":"GET","description":"One page of Intercom conversations as the signed-in user (pass ?cursor= from the previous page's nextCursor to continue).","successSchema":{"type":"object","properties":{"conversations":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"state":{"type":"string"},"open":{"type":"boolean"},"createdAt":{"type":"integer","description":"Unix seconds."},"updatedAt":{"type":"integer","description":"Unix seconds."}},"required":["id","state"]}},"nextCursor":{"type":["string","null"]}},"required":["conversations","nextCursor"]},"errors":[{"status":401,"error":"unauthorized"},{"status":401,"error":"intercom_not_connected"},{"status":503,"error":"intercom_not_configured"}]},{"path":"/vendors/hubspot/companies","method":"GET","description":"Companies read directly from the HubSpot portal. Unpaginated — the whole list in one response. For the normalised, paginated view with ARR and the district/school hierarchy, use /crm/customers/companies instead.","successSchema":{"type":"object","properties":{"companies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"domain":{"type":"string"},"industry":{"type":"string"},"employees":{"type":"number"}}}}}},"errors":[{"status":401,"error":"unauthorized"},{"status":503,"error":"hubspot_not_configured"}]},{"path":"/vendors/hubspot/contacts","method":"GET","description":"Contacts read directly from the HubSpot portal. Unpaginated. `companyId` joins to /vendors/hubspot/companies.","successSchema":{"type":"object","properties":{"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"companyId":{"type":"string","nullable":true}}}}}},"errors":[{"status":401,"error":"unauthorized"},{"status":503,"error":"hubspot_not_configured"}]},{"path":"/vendors/hubspot/deals","method":"GET","description":"Deals read directly from the HubSpot portal. Unpaginated. `companyId` is null for a deal not attached to a company.","successSchema":{"type":"object","properties":{"deals":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"companyId":{"type":"string","nullable":true},"amount":{"type":"number"},"stage":{"type":"string"},"closeDate":{"type":"string"}}}}}},"errors":[{"status":401,"error":"unauthorized"},{"status":503,"error":"hubspot_not_configured"}]},{"path":"/cx/owners","method":"GET","description":"The CSM roster: owner id → name/email. Pass ?activeOnly=true to drop departed owners. Owner ids join to /cx/book?owner=.","successSchema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"data":{"type":"object","properties":{"owners":{"type":"array","items":{"type":"object","properties":{"ownerId":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"}}}}}}},"required":["ok","data"]},{"type":"object","description":"This deployment cannot answer yet — render `waitingOn`, not zeroes.","properties":{"ok":{"const":false},"waitingOn":{"type":"string","description":"The capability or view still unwired."}},"required":["ok","waitingOn"]}]},"errors":[{"status":401,"error":"unauthorized"},{"status":403,"error":"forbidden"}]},{"path":"/cx/book","method":"GET","description":"One CSM's book of business, fully joined — the accounts assigned to ?owner=<ownerId>, each with health, silence, tier, ARR, enrolment and reach. This is the endpoint a CSM dashboard wants — one row per COMPANY. Per-district figures (sites tracked, silent sites, average health, at-risk count) are a group-by on `districtName` over these rows; the endpoint does not pre-aggregate them. Window via ?days= (default 30) or ?from=&to= (YYYY-MM-DD). ?basis=real (default) uses the actual HubSpot book assignment; ?basis=activity is a DEMO stand-in that groups by who logged a touch — \"who logged the touch\" is NOT \"who owns the account\", so a dashboard using it MUST label it as derived. The response echoes `basis` back so you can.","successSchema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"data":{"type":"object","properties":{"ownerId":{"type":"string"},"basis":{"type":"string","enum":["real","activity"]},"window":{"type":"object","properties":{"sinceIso":{"type":"string"},"untilIso":{"type":"string"}}},"rows":{"type":"array","items":{"type":"object","properties":{"companyId":{"type":"string","description":"HubSpot company id — the join key."},"mingaId":{"type":"string","nullable":true,"description":"Product school id, or null."},"schoolName":{"type":"string"},"districtName":{"type":"string"},"enrolment":{"type":"number","nullable":true},"arr":{"type":"number","nullable":true,"description":"Annual recurring revenue. DISTRICT-GRAIN: never sum this across the schools of one district — the same figure is carried on each, so a sum multiplies it."},"tier":{"type":"string","description":"Subscription/customer tier."},"churnedDate":{"type":"string","nullable":true},"health":{"type":"object","nullable":true,"description":"Health for this company. Null when no health source is wired.","properties":{"series":{"type":"array","description":"The dated points behind the band, for a sparkline.","items":{"type":"object","properties":{"date":{"type":"string"},"score":{"type":"number","nullable":true}}}},"latest":{"type":"object","nullable":true,"properties":{"date":{"type":"string"},"score":{"type":"number","nullable":true}}},"band":{"type":"string","enum":["STRONG","GOOD","MODERATE","POOR","NO_BASELINE"]}}},"cadence":{"type":"object","nullable":true,"description":"Outreach against this company in the window. Null when nothing is wired.","properties":{"lastTouchDate":{"type":"string","nullable":true,"description":"YYYY-MM-DD, or null."},"daysSinceContact":{"type":"number","nullable":true,"description":"Days between the last touch and the WINDOW END (not today). Null means never touched within the window — which is not the same as never touched at all, so do not render it as \"never\"."},"byKind":{"type":"object","description":"Counts per engagement kind (call / meeting / email) within the window.","additionalProperties":{"type":"number"}},"silent30d":{"type":"boolean","description":"No call, meeting or email in the trailing 30 days of the window."}}},"reach":{"type":"number","nullable":true,"description":"Latest-week active persons ÷ enrolment (0–1); null when enrolment is unknown."},"reachPercentile":{"type":"number","nullable":true},"priority":{"type":"object","description":"A HEURISTIC triage rank within this book — a product judgement, not an SLA. Do not present it as a guarantee, a target, or a score the customer earns.","properties":{"score":{"type":"number"},"percentile":{"type":"number","description":"Rank within THIS book (1–100)."},"reasons":{"type":"array","items":{"type":"string"},"description":"Why this account ranked where it did — show these, not just the number."}}}},"required":["companyId","schoolName","districtName"]}}}}},"required":["ok","data"]},{"type":"object","description":"This deployment cannot answer yet — render `waitingOn`, not zeroes.","properties":{"ok":{"const":false},"waitingOn":{"type":"string","description":"The capability or view still unwired."}},"required":["ok","waitingOn"]}]},"errors":[{"status":401,"error":"unauthorized"},{"status":403,"error":"forbidden"},{"status":400,"error":"owner_required"}]},{"path":"/cx/districts","method":"GET","description":"District-grain health and cadence rollup across every district, for the window (?days= or ?from=&to=). One row per district, so ARR here is already at the right grain — unlike the per-school rows in /cx/book.","successSchema":{"oneOf":[{"type":"object","properties":{"ok":{"const":true},"data":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"health":{"type":"object","nullable":true,"description":"Health for this company. Null when no health source is wired.","properties":{"series":{"type":"array","description":"The dated points behind the band, for a sparkline.","items":{"type":"object","properties":{"date":{"type":"string"},"score":{"type":"number","nullable":true}}}},"latest":{"type":"object","nullable":true,"properties":{"date":{"type":"string"},"score":{"type":"number","nullable":true}}},"band":{"type":"string","enum":["STRONG","GOOD","MODERATE","POOR","NO_BASELINE"]}}},"cadence":{"type":"object","nullable":true,"description":"Outreach against this company in the window. Null when nothing is wired.","properties":{"lastTouchDate":{"type":"string","nullable":true,"description":"YYYY-MM-DD, or null."},"daysSinceContact":{"type":"number","nullable":true,"description":"Days between the last touch and the WINDOW END (not today). Null means never touched within the window — which is not the same as never touched at all, so do not render it as \"never\"."},"byKind":{"type":"object","description":"Counts per engagement kind (call / meeting / email) within the window.","additionalProperties":{"type":"number"}},"silent30d":{"type":"boolean","description":"No call, meeting or email in the trailing 30 days of the window."}}}}}}}}},"required":["ok","data"]},{"type":"object","description":"This deployment cannot answer yet — render `waitingOn`, not zeroes.","properties":{"ok":{"const":false},"waitingOn":{"type":"string","description":"The capability or view still unwired."}},"required":["ok","waitingOn"]}]},"errors":[{"status":401,"error":"unauthorized"},{"status":403,"error":"forbidden"}]}]}