{"openapi":"3.0.3","info":{"title":"Holoscope — NSA SALES BOOST","version":"0.1.0","description":"The holoscope of the Cercles circle « NSA SALES BOOST »: it helps the circle set its sales goal and keep it.\n\nEverything the web front-end does is possible through this API. This document is public; the data is not.\nData routes sit behind the gate: only members of the circle, as verified with Cercles at call time, get through.\nDeal records are read inside the holoscope only, one page at a time: no bulk export. Nothing is ever measured per person.\n\nErrors share one shape: `{ \"error\": { \"code\", \"message\" } }`."},"components":{"securitySchemes":{"member":{"type":"http","scheme":"bearer","description":"Proof of circle membership (mechanism pending the circle's decision; refused until configured)."}},"schemas":{}},"paths":{"/api/health":{"get":{"summary":"Is the server up, and is the gate configured?","tags":["service"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"gateConfigured":{"type":"boolean"},"version":{"type":"string"}}}}}}}}},"/api/goal":{"get":{"summary":"The yearly booking goal, the latest booking aggregate, and the pace","tags":["goal"],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}},"put":{"summary":"Set or replace the yearly booking goal","tags":["goal"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["title","targetAmount","currency","periodStart","periodEnd"],"properties":{"title":{"type":"string","minLength":1,"maxLength":200,"description":"Shown as the goal title; mirrors the Cercles goal."},"targetAmount":{"type":"number","exclusiveMinimum":0,"description":"Bookings (POs signed) to reach over the period."},"currency":{"type":"string","enum":["USD","EUR"]},"periodStart":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"First day of the period."},"periodEnd":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"Last day of the period."},"cerclesGoalId":{"type":["string","null"],"format":"uuid","description":"The goal in Cercles this one mirrors, once set."}},"additionalProperties":false}}},"required":true},"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/steps":{"get":{"summary":"The steps of the sales process, in order","tags":["goal"],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}},"put":{"summary":"Define the steps of the sales process, in order (replaces the list)","tags":["goal"],"requestBody":{"content":{"application/json":{"schema":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"object","required":["key","label"],"additionalProperties":false,"properties":{"key":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$","description":"Key of a step of the sales process (see GET /api/steps)."},"label":{"type":"string","minLength":1,"maxLength":80}}}}}}},"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/funnel":{"get":{"summary":"Per-step aggregates for one week (latest if omitted); empty cells stay null","tags":["measures"],"parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"query","name":"weekOf","required":false}],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/funnel/{weekOf}/{stepKey}":{"put":{"summary":"Record the aggregate of one step for one week","tags":["measures"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"inProgress":{"type":["number","null"],"minimum":0,"multipleOf":1,"description":"Opportunities currently at this step."},"passRate":{"type":["number","null"],"minimum":0,"maximum":1,"description":"Share that move on to the next step (0–1)."},"avgDurationDays":{"type":["number","null"],"minimum":0,"description":"Average days spent at this step."},"source":{"type":"string","minLength":1,"maxLength":80,"description":"Where the aggregate comes from (e.g. \"Monday, BD weekly review\")."}},"required":["source"],"additionalProperties":false}}},"required":true},"parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"path","name":"weekOf","required":true},{"schema":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$"},"in":"path","name":"stepKey","required":true,"description":"Key of a step of the sales process (see GET /api/steps)."}],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/bookings":{"get":{"summary":"Weekly booking aggregates, most recent first","tags":["measures"],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/bookings/{weekOf}":{"put":{"summary":"Record the booking aggregate of one week","tags":["measures"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["wonToDate","source"],"additionalProperties":false,"properties":{"wonToDate":{"type":"number","minimum":0,"description":"POs signed since the start of the fiscal year."},"weightedPipeline":{"type":["number","null"],"minimum":0},"unquotedPipeline":{"type":["number","null"],"minimum":0},"source":{"type":"string","minLength":1,"maxLength":80}}}}},"required":true},"parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"in":"path","name":"weekOf","required":true}],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/canal":{"get":{"summary":"The canal: sea, locks (flow), basins (level, marks, median days, conversion) and harbour (gap to pace)","tags":["measures"],"description":"Rates and medians are null below 10 deals over the window, per lock and per filtered view. A lock not yet fed by a source is \"measured: false\" and the basins it separates are merged.","parameters":[{"schema":{"type":"string","maxLength":40},"in":"query","name":"lab","required":false},{"schema":{"type":"string","maxLength":40},"in":"query","name":"area","required":false},{"schema":{"type":"string","enum":["key","minor"]},"in":"query","name":"profile","required":false},{"schema":{"type":"string","enum":["recurring","reactivated","new"]},"in":"query","name":"clientType","required":false}],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/canal/basins/{key}/deals":{"get":{"summary":"The deals in one basin, one page at a time (no bulk export)","tags":["measures"],"parameters":[{"schema":{"type":"string","maxLength":40},"in":"query","name":"lab","required":false},{"schema":{"type":"string","maxLength":40},"in":"query","name":"area","required":false},{"schema":{"type":"string","enum":["key","minor"]},"in":"query","name":"profile","required":false},{"schema":{"type":"string","enum":["recurring","reactivated","new"]},"in":"query","name":"clientType","required":false},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":25},"in":"query","name":"limit","required":false},{"schema":{"type":"integer","minimum":0,"default":0},"in":"query","name":"offset","required":false},{"schema":{"type":"string","pattern":"^[a-z_]+__[a-z_]+$"},"in":"path","name":"key","required":true,"description":"Basin key: \"<entry lock>__<exit lock>\", as listed by GET /api/canal."}],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/canal/locks/{key}":{"put":{"summary":"Declare that a source now feeds a lock, from a given day (or null: not measured)","tags":["measures"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["measuredSince"],"properties":{"measuredSince":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"source":{"type":["string","null"],"maxLength":80,"description":"E.g. \"Monday · Quote Production · Date Requested\"."}}}}},"required":true},"parameters":[{"schema":{"type":"string","enum":["discovery","quote_requested","quote_sent","msa","po"]},"in":"path","name":"key","required":true}],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/canal/basins/{key}/marks":{"put":{"summary":"Set the high and low marks of a basin by hand (shown as \"set by the circle\")","tags":["measures"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["high","low"],"properties":{"high":{"type":"number","minimum":0},"low":{"type":"number","minimum":0}}}}},"required":true},"parameters":[{"schema":{"type":"string","pattern":"^[a-z_]+__[a-z_]+$"},"in":"path","name":"key","required":true,"description":"Basin key: \"<entry lock>__<exit lock>\", as listed by GET /api/canal."}],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}},"delete":{"summary":"Go back to computed marks for a basin","tags":["measures"],"parameters":[{"schema":{"type":"string","pattern":"^[a-z_]+__[a-z_]+$"},"in":"path","name":"key","required":true,"description":"Basin key: \"<entry lock>__<exit lock>\", as listed by GET /api/canal."}],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/demo/canal":{"get":{"summary":"The canal on FICTITIOUS deals (public)","tags":["demo"],"parameters":[{"schema":{"type":"string","maxLength":40},"in":"query","name":"lab","required":false},{"schema":{"type":"string","maxLength":40},"in":"query","name":"area","required":false},{"schema":{"type":"string","enum":["key","minor"]},"in":"query","name":"profile","required":false},{"schema":{"type":"string","enum":["recurring","reactivated","new"]},"in":"query","name":"clientType","required":false}],"responses":{"200":{"description":"Default Response"}}}},"/api/demo/canal/basins/{key}/deals":{"get":{"summary":"The FICTITIOUS deals of one basin (public)","tags":["demo"],"parameters":[{"schema":{"type":"string","maxLength":40},"in":"query","name":"lab","required":false},{"schema":{"type":"string","maxLength":40},"in":"query","name":"area","required":false},{"schema":{"type":"string","enum":["key","minor"]},"in":"query","name":"profile","required":false},{"schema":{"type":"string","enum":["recurring","reactivated","new"]},"in":"query","name":"clientType","required":false},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":25},"in":"query","name":"limit","required":false},{"schema":{"type":"integer","minimum":0,"default":0},"in":"query","name":"offset","required":false},{"schema":{"type":"string","pattern":"^[a-z_]+__[a-z_]+$"},"in":"path","name":"key","required":true,"description":"Basin key: \"<entry lock>__<exit lock>\", as listed by GET /api/canal."}],"responses":{"200":{"description":"Default Response"}}}},"/api/actions":{"get":{"summary":"All actions, newest first","tags":["actions"],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}},"post":{"summary":"Add an action tied to a step","tags":["actions"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200,"description":"What will be done. No client name: a programme code is enough."},"stepKey":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$","description":"The step of the sales process this action should move."},"ownerMemberId":{"type":["string","null"],"format":"uuid","description":"Cercles member id of whoever holds it; null if nobody yet."},"status":{"type":"string","enum":["proposed","in_progress","done","dropped"]},"cerclesStepId":{"type":["string","null"],"format":"uuid","description":"The Cercles « step » this action mirrors, if any."}},"additionalProperties":false,"required":["title","stepKey"]}}},"required":true},"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/actions/{id}":{"patch":{"summary":"Change an action (owner, status, step…)","tags":["actions"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200,"description":"What will be done. No client name: a programme code is enough."},"stepKey":{"type":"string","pattern":"^[a-z][a-z0-9_]{0,31}$","description":"The step of the sales process this action should move."},"ownerMemberId":{"type":["string","null"],"format":"uuid","description":"Cercles member id of whoever holds it; null if nobody yet."},"status":{"type":"string","enum":["proposed","in_progress","done","dropped"]},"cerclesStepId":{"type":["string","null"],"format":"uuid","description":"The Cercles « step » this action mirrors, if any."}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"integer","minimum":1},"in":"path","name":"id","required":true}],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/review":{"get":{"summary":"The weekly review: gap to pace, empty cells, actions without owner","tags":["review"],"security":[{"member":[]}],"responses":{"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/access":{"get":{"summary":"The gate rule and the history of its proofs (public)","tags":["access"],"responses":{"200":{"description":"Default Response"}}}}},"servers":[],"tags":[{"name":"goal","description":"Tab 1 — the yearly booking goal and the sales process"},{"name":"measures","description":"Tab 2 — the canal (basins and locks) and its records"},{"name":"actions","description":"Tab 3 — actions, each tied to the step it should move"},{"name":"review","description":"Tab 4 — the weekly review, computed"},{"name":"access","description":"Tab 5 — the gate and its proof"},{"name":"demo","description":"The canal on fictitious deals, public, to try the screen while the gate is closed"},{"name":"service","description":"Health and documentation (public)"}]}