@thinkai/tai-api-contract 2.182.0 → 2.184.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  openapi: 3.0.3
2
2
  info:
3
3
  title: ThinkAI API
4
- version: 2.182.0
4
+ version: 2.184.0
5
5
  description: >
6
6
  Contract surface for the AI Driven SDLC backend used by ThinkAI.
7
7
  Workspace-scoped routes use `/workspaces/{workspaceId}/...`.
@@ -65,6 +65,8 @@ tags:
65
65
  AI-tool usage/spend, org chart, and readiness — never mock payloads.
66
66
  - name: WorkspaceActivity
67
67
  description: Workspace-wide lifecycle activity log — repo add/remove/re-add and integration lifecycle events (issue #515).
68
+ - name: AssessmentReports
69
+ description: Encrypted stored assessment buyer PDFs, secure link delivery, and hide/restore (issue #486).
68
70
  - name: PlatformAdmin
69
71
  description: Platform-admin operations restricted to PLATFORM_ADMIN_EMAILS. Callers must present a valid JWT from an email on that allowlist; all other callers receive 403.
70
72
  - name: HRIS
@@ -430,7 +432,10 @@ paths:
430
432
  are ignored, so an unscanned source cannot be marked fresh. The
431
433
  five-business-day clock uses Monday–Friday in America/New_York and is
432
434
  stored on the first passing result. A later blocker does not clear it.
433
- Active-engineer counts stay unloaded until stored activity can be counted.
435
+ The active-engineer count is loaded from stored commit authors and merged
436
+ pull requests in the 90-day baseline window (bots and identities not on the
437
+ org chart excluded). It stays unloaded until commit ingest covers the whole
438
+ window for every non-archived repository.
434
439
 
435
440
  **Authorization:** Bearer JWT required. Caller email must appear in `PLATFORM_ADMIN_EMAILS`.
436
441
  parameters:
@@ -602,7 +607,16 @@ paths:
602
607
  items:
603
608
  type: object
604
609
  required:
605
- [workspaceId, offerId, ready, blocker, nextAction, dueDate, evaluatedAt]
610
+ [
611
+ workspaceId,
612
+ offerId,
613
+ ready,
614
+ blocker,
615
+ nextAction,
616
+ clockStartedOn,
617
+ dueDate,
618
+ evaluatedAt,
619
+ ]
606
620
  properties:
607
621
  workspaceId:
608
622
  type: string
@@ -625,6 +639,11 @@ paths:
625
639
  href:
626
640
  type: string
627
641
  nullable: true
642
+ clockStartedOn:
643
+ type: string
644
+ nullable: true
645
+ format: date
646
+ description: America/New_York day the five-business-day clock started.
628
647
  dueDate:
629
648
  type: string
630
649
  nullable: true
@@ -668,174 +687,14 @@ paths:
668
687
  content:
669
688
  application/json:
670
689
  schema:
671
- type: object
672
- required:
673
- - manifest
674
- - workspaceId
675
- - expectedWorkspaceId
676
- - expectedRepositoryIds
677
- - soldWindowComplete
678
- - premiumAiAssessable
679
- - namedPersonSectionIds
680
- - tolerances
681
- properties:
682
- manifest:
683
- type: object
684
- required:
685
- - offerId
686
- - dictionaryVersion
687
- - validation
688
- - shareMode
689
- - watermark
690
- - scope
691
- - windows
692
- - sources
693
- - consent
694
- - sections
695
- properties:
696
- offerId:
697
- type: string
698
- enum: [engineering_readiness_baseline, agentic_ai_roi]
699
- dictionaryVersion:
700
- type: string
701
- validation:
702
- type: string
703
- enum: [validated, not_validated]
704
- shareMode:
705
- type: string
706
- enum: [internal, external]
707
- watermark:
708
- type: string
709
- nullable: true
710
- enum: ["NOT VALIDATED", "SAMPLE · SYNTHETIC DATA"]
711
- scope:
712
- type: object
713
- required: [repositories, excluded]
714
- properties:
715
- repositories:
716
- type: array
717
- items:
718
- type: object
719
- required: [id, name]
720
- properties:
721
- id: { type: string }
722
- name: { type: string }
723
- excluded:
724
- type: array
725
- items:
726
- type: object
727
- required: [id, name, reason]
728
- properties:
729
- id: { type: string }
730
- name: { type: string }
731
- reason: { type: string }
732
- windows:
733
- type: object
734
- required: [pre, post, transitionDays]
735
- properties:
736
- pre:
737
- type: object
738
- nullable: true
739
- required: [start, end]
740
- properties:
741
- start: { type: string }
742
- end: { type: string }
743
- post:
744
- type: object
745
- nullable: true
746
- required: [start, end]
747
- properties:
748
- start: { type: string }
749
- end: { type: string }
750
- transitionDays:
751
- type: integer
752
- sources:
753
- type: array
754
- items:
755
- type: object
756
- required: [provider, status, freshAt]
757
- properties:
758
- provider: { type: string }
759
- status: { type: string }
760
- freshAt: { type: string, nullable: true }
761
- consent:
762
- type: object
763
- required: [aggregateUse, recordedAt]
764
- properties:
765
- aggregateUse:
766
- type: string
767
- enum: [opt_in, opt_out, unset]
768
- recordedAt:
769
- type: string
770
- nullable: true
771
- sections:
772
- type: array
773
- items:
774
- type: object
775
- required: [id, status]
776
- properties:
777
- id: { type: string }
778
- status:
779
- type: string
780
- enum: [ok, failed, omitted]
781
- reason: { type: string }
782
- workspaceId:
783
- type: string
784
- expectedWorkspaceId:
785
- type: string
786
- expectedRepositoryIds:
787
- type: array
788
- items:
789
- type: string
790
- soldWindowComplete:
791
- type: boolean
792
- premiumAiAssessable:
793
- type: boolean
794
- namedPersonSectionIds:
795
- type: array
796
- items:
797
- type: string
798
- tolerances:
799
- type: array
800
- items:
801
- type: object
802
- required: [id, status]
803
- properties:
804
- id:
805
- type: string
806
- status:
807
- type: string
808
- enum: [unset, exceeded, within]
690
+ $ref: "#/components/schemas/AssessmentManifestValidationBodyDto"
809
691
  responses:
810
692
  "200":
811
693
  description: Validation result. Client validation and watermark inputs are ignored.
812
694
  content:
813
695
  application/json:
814
696
  schema:
815
- type: object
816
- required: [buyerDeliverable, validation, watermark, reasons, contentHash]
817
- properties:
818
- buyerDeliverable:
819
- type: boolean
820
- validation:
821
- type: string
822
- enum: [validated, not_validated]
823
- watermark:
824
- type: string
825
- nullable: true
826
- enum: ["NOT VALIDATED"]
827
- reasons:
828
- type: array
829
- items:
830
- type: object
831
- required: [code, message]
832
- properties:
833
- code:
834
- type: string
835
- message:
836
- type: string
837
- contentHash:
838
- type: string
697
+ $ref: "#/components/schemas/AssessmentManifestValidationResultDto"
839
698
  "400":
840
699
  description: Body failed validation.
841
700
  content:
@@ -861,6 +720,180 @@ paths:
861
720
  error: Forbidden
862
721
  code: forbidden
863
722
 
723
+ /workspaces/{workspaceId}/assessment-manifest/validate:
724
+ post:
725
+ tags: [Workspace]
726
+ summary: Validate this workspace's assessment manifest
727
+ operationId: validateWorkspaceAssessmentManifest
728
+ description: >
729
+ Runs the same fail-closed manifest check as `runAssessmentManifestValidation`
730
+ for the workspace in the path. The server ignores `manifest.validation` and
731
+ `manifest.watermark` from the client. `buyerDeliverable` is true only when
732
+ the result is validated and `shareMode` is internal. An external share-safe
733
+ result can be validated and is never the buyer file. Unset or exceeded
734
+ tolerances fail. No numeric tolerance is assumed. GitLab and Bitbucket
735
+ fixtures are not part of this operation.
736
+
737
+ `workspaceId` and `expectedWorkspaceId` in the body must equal the path
738
+ workspace. A body that names another workspace is rejected. Comparing two
739
+ workspaces stays on `runAssessmentManifestValidation`.
740
+
741
+ **Authorization:** Bearer JWT required. Caller must be a workspace admin,
742
+ or a platform admin who is a member of this workspace. Members who are not
743
+ workspace admins receive 403. Non-members of an existing workspace receive
744
+ 403. Unknown workspaces receive 404.
745
+ parameters:
746
+ - $ref: "#/components/parameters/WorkspaceId"
747
+ requestBody:
748
+ required: true
749
+ content:
750
+ application/json:
751
+ schema:
752
+ $ref: "#/components/schemas/AssessmentManifestValidationBodyDto"
753
+ responses:
754
+ "200":
755
+ description: Validation result. Client validation and watermark inputs are ignored.
756
+ content:
757
+ application/json:
758
+ schema:
759
+ $ref: "#/components/schemas/AssessmentManifestValidationResultDto"
760
+ "400":
761
+ description: Body failed validation.
762
+ content:
763
+ application/json:
764
+ schema:
765
+ $ref: "#/components/schemas/ErrorMessageDto"
766
+ example:
767
+ error: manifest.offerId is not a known assessment offer
768
+ code: invalid_body
769
+ "401":
770
+ $ref: "#/components/responses/Unauthorized"
771
+ "403":
772
+ description: Caller cannot validate this workspace.
773
+ content:
774
+ application/json:
775
+ schema:
776
+ $ref: "#/components/schemas/ErrorMessageDto"
777
+ examples:
778
+ not_member:
779
+ value:
780
+ error: Forbidden
781
+ workspace_admin_required:
782
+ value:
783
+ error: Forbidden
784
+ code: workspace_admin_required
785
+ cross_workspace_forbidden:
786
+ value:
787
+ error: Forbidden
788
+ code: cross_workspace_forbidden
789
+ "404":
790
+ description: Workspace does not exist.
791
+ content:
792
+ application/json:
793
+ schema:
794
+ $ref: "#/components/schemas/ErrorMessageDto"
795
+ example:
796
+ error: Not found
797
+
798
+ /workspaces/{workspaceId}/assessment-manifest/build:
799
+ post:
800
+ tags: [Workspace]
801
+ summary: Build and validate this workspace's assessment manifest from stored evidence
802
+ operationId: buildWorkspaceAssessmentManifest
803
+ description: >
804
+ The client sends only the offer, share mode, the render status of each selected
805
+ section, and the matched-period ROI chapter it printed. The server fills scope,
806
+ excluded repositories, windows, sources with freshness, consent,
807
+ `expectedRepositoryIds`, `soldWindowComplete`, `premiumAiAssessable`, and
808
+ tolerances from stored data, then runs the same fail-closed check as
809
+ `validateWorkspaceAssessmentManifest`.
810
+
811
+
812
+ Scope is the non-archived workspace repositories. Above 50 repositories or 500
813
+ active engineers fails (custom quote). For the premium offer, repositories without
814
+ merged pull requests in both matched windows are listed in `excluded` and left out
815
+ of scope. Baseline windows: `pre` is null, `post` is the 90 calendar days ending
816
+ yesterday (America/New_York), and `transitionDays` is 0. Premium windows come from
817
+ the confirmed rollout day. Freshness: source control 24 hours, AI usage 7 days,
818
+ readiness scans 30 days. Aggregate-use consent is reported as stored; no row is
819
+ `unset`.
820
+
821
+
822
+ `premiumAiAssessable` needs fresh Cursor, Claude, or Copilot usage in the post
823
+ window and complete matched-period evidence. A live ROI refusal for incomplete
824
+ data blocks validation (`rollout_unset`, `window_missing`,
825
+ `ship_weight_incomplete`, `active_engineers_unknown`, `confounder_inputs_missing`,
826
+ `normalization_unset`, `output_unit_mismatch`). A refusal that is a legitimate
827
+ result does not (`non_positive_output`, `confounder`, `missing_cost`,
828
+ `non_usd_cost`, `cost_not_attributable`); the report prints "not assessable" with
829
+ no figure. The premium `roi` tolerance is `unset` when the ROI section did not
830
+ render or `roi` is missing, `exceeded` when the printed chapter differs from the
831
+ server result (USD figures within the dictionary money tolerance), and `within`
832
+ otherwise. Named-person sections always fail.
833
+
834
+
835
+ Send `result.contentHash` with the PDF to `storeAssessmentReport`, which rebuilds
836
+ the manifest and stores only when it is still a validated internal buyer file
837
+ with the same hash.
838
+
839
+
840
+ **Authorization:** Bearer JWT required. Caller must be a workspace admin, or a
841
+ platform admin who is a member of this workspace. Members who are not workspace
842
+ admins receive 403. Non-members of an existing workspace receive 403. Unknown
843
+ workspaces receive 404.
844
+ parameters:
845
+ - $ref: "#/components/parameters/WorkspaceId"
846
+ requestBody:
847
+ required: true
848
+ content:
849
+ application/json:
850
+ schema:
851
+ $ref: "#/components/schemas/BuildAssessmentManifestBodyDto"
852
+ responses:
853
+ "200":
854
+ description: Server-built manifest body and its validation result.
855
+ content:
856
+ application/json:
857
+ schema:
858
+ $ref: "#/components/schemas/BuildAssessmentManifestResponseDto"
859
+ "400":
860
+ description: Body failed validation.
861
+ content:
862
+ application/json:
863
+ schema:
864
+ $ref: "#/components/schemas/ErrorMessageDto"
865
+ example:
866
+ error: offerId is not a known assessment offer
867
+ code: invalid_body
868
+ "401":
869
+ $ref: "#/components/responses/Unauthorized"
870
+ "403":
871
+ description: Caller cannot build this workspace's manifest.
872
+ content:
873
+ application/json:
874
+ schema:
875
+ $ref: "#/components/schemas/ErrorMessageDto"
876
+ example:
877
+ error: Forbidden
878
+ code: workspace_admin_required
879
+ "404":
880
+ description: Workspace does not exist.
881
+ content:
882
+ application/json:
883
+ schema:
884
+ $ref: "#/components/schemas/ErrorMessageDto"
885
+ example:
886
+ error: Not found
887
+ "503":
888
+ description: Stored evidence could not be loaded (no database or a load failure).
889
+ content:
890
+ application/json:
891
+ schema:
892
+ $ref: "#/components/schemas/ErrorMessageDto"
893
+ example:
894
+ error: Service unavailable
895
+ code: evidence_unavailable
896
+
864
897
  /admin/readiness/token-usage:
865
898
  get:
866
899
  tags: [PlatformAdmin]
@@ -2454,6 +2487,7 @@ paths:
2454
2487
  - $ref: "#/components/schemas/ClaudeSourceDto"
2455
2488
  - $ref: "#/components/schemas/CodeIntelligenceSourcePatchDto"
2456
2489
  - $ref: "#/components/schemas/NewRelicSourceDto"
2490
+ - $ref: "#/components/schemas/DatadogSourceDto"
2457
2491
  - $ref: "#/components/schemas/PrometheusSourceDto"
2458
2492
  - $ref: "#/components/schemas/GrafanaSourceDto"
2459
2493
  - $ref: "#/components/schemas/SonarQubeSourceDto"
@@ -2575,6 +2609,7 @@ paths:
2575
2609
  - $ref: "#/components/schemas/ClaudeSourceDto"
2576
2610
  - $ref: "#/components/schemas/CodeIntelligenceSourcePatchDto"
2577
2611
  - $ref: "#/components/schemas/NewRelicSourceDto"
2612
+ - $ref: "#/components/schemas/DatadogSourceDto"
2578
2613
  - $ref: "#/components/schemas/PrometheusSourceDto"
2579
2614
  - $ref: "#/components/schemas/GrafanaSourceDto"
2580
2615
  - $ref: "#/components/schemas/SonarQubeSourceDto"
@@ -2604,6 +2639,13 @@ paths:
2604
2639
  baseUrl: https://sonarqube.example.com
2605
2640
  token: sonarqube_api_token_example
2606
2641
  projectKey: my-project
2642
+ datadogValid:
2643
+ summary: Datadog source for connection test (US1)
2644
+ value:
2645
+ type: datadog
2646
+ apiKey: datadog_api_key_example
2647
+ appKey: datadog_app_key_example
2648
+ site: datadoghq.com
2607
2649
  githubShape:
2608
2650
  summary: GitHub-shaped body (non-AI-tool types return unsupported_type unless extended)
2609
2651
  value:
@@ -7154,26 +7196,25 @@ paths:
7154
7196
  schema:
7155
7197
  $ref: "#/components/schemas/ErrorMessageDto"
7156
7198
 
7157
- /workspaces/{workspaceId}/aggregate-use-consent:
7199
+ /workspaces/{workspaceId}/assessment-rollout:
7158
7200
  get:
7159
7201
  tags: [Workspace]
7160
- summary: Read anonymized aggregate-use permission
7161
- operationId: getAggregateUseConsent
7202
+ summary: Read the customer-confirmed assessment rollout day
7203
+ operationId: getWorkspaceAssessmentRollout
7162
7204
  description: >
7163
- Returns whether ThinkAI may use this workspace's anonymized aggregate results in
7164
- marketing and peer benchmarks. A workspace that never chose returns `unset`, which
7165
- behaves as opted out (`effective: false`). The permission never covers company name,
7166
- logo, quote, raw report, repository or person identity, or customer reference.
7167
- Any workspace member may read it.
7205
+ Returns the AI rollout day the customer confirmed for the Agentic AI ROI
7206
+ assessment, interpreted at 00:00 America/New_York, plus who set it and when.
7207
+ This is the only rollout source for the matched pre and post windows.
7208
+ `rolloutDay` is null until an admin sets it. Any workspace member may read it.
7168
7209
  parameters:
7169
7210
  - $ref: "#/components/parameters/WorkspaceId"
7170
7211
  responses:
7171
7212
  "200":
7172
- description: Current permission and the sentence reports must print
7213
+ description: Current rollout day
7173
7214
  content:
7174
7215
  application/json:
7175
7216
  schema:
7176
- $ref: "#/components/schemas/AggregateUseConsentDto"
7217
+ $ref: "#/components/schemas/AssessmentRolloutDto"
7177
7218
  "401":
7178
7219
  $ref: "#/components/responses/Unauthorized"
7179
7220
  "403":
@@ -7184,17 +7225,21 @@ paths:
7184
7225
  application/json:
7185
7226
  schema:
7186
7227
  $ref: "#/components/schemas/ErrorMessageDto"
7228
+ "503":
7229
+ description: Rollout storage is not configured
7230
+ content:
7231
+ application/json:
7232
+ schema:
7233
+ $ref: "#/components/schemas/ErrorMessageDto"
7187
7234
  put:
7188
7235
  tags: [Workspace]
7189
- summary: Set anonymized aggregate-use permission
7190
- operationId: putAggregateUseConsent
7236
+ summary: Set the customer-confirmed assessment rollout day
7237
+ operationId: putWorkspaceAssessmentRollout
7191
7238
  description: >
7192
- Workspace admins opt in or out. Every change writes one
7193
- `workspace.aggregate_use.changed` activity-log row in the same transaction.
7194
- Moving from `opt_in` to `opt_out` records a withdrawal and stops future extraction.
7195
- Opt-in requires the current legal-approved notice version; while no notice is
7196
- approved, opt-in returns `409 aggregate_use_notice_unapproved`. Opt-out is always
7197
- accepted. A refusal does not change price, delivery, support, or assessment output.
7239
+ Workspace admins, or platform admins who are workspace members, set the rollout
7240
+ day. The day must be a real calendar date that has already started in
7241
+ America/New_York. Each save records the caller and time and writes one
7242
+ `workspace.assessment_rollout.changed` activity-log row.
7198
7243
  parameters:
7199
7244
  - $ref: "#/components/parameters/WorkspaceId"
7200
7245
  requestBody:
@@ -7202,16 +7247,16 @@ paths:
7202
7247
  content:
7203
7248
  application/json:
7204
7249
  schema:
7205
- $ref: "#/components/schemas/AggregateUseConsentPutDto"
7250
+ $ref: "#/components/schemas/AssessmentRolloutPutDto"
7206
7251
  responses:
7207
7252
  "200":
7208
- description: Saved permission
7253
+ description: Saved rollout day
7209
7254
  content:
7210
7255
  application/json:
7211
7256
  schema:
7212
- $ref: "#/components/schemas/AggregateUseConsentDto"
7257
+ $ref: "#/components/schemas/AssessmentRolloutDto"
7213
7258
  "400":
7214
- description: "Invalid body (`code`: `invalid_input`)"
7259
+ description: "Invalid body, impossible date, or future day (`code`: `invalid_input`)"
7215
7260
  content:
7216
7261
  application/json:
7217
7262
  schema:
@@ -7230,45 +7275,38 @@ paths:
7230
7275
  application/json:
7231
7276
  schema:
7232
7277
  $ref: "#/components/schemas/ErrorMessageDto"
7233
- "409":
7234
- description: >
7235
- Opt-in refused because no notice is approved (`aggregate_use_notice_unapproved`)
7236
- or the submitted notice version is not current (`aggregate_use_notice_stale`).
7278
+ "503":
7279
+ description: Rollout storage is not configured
7237
7280
  content:
7238
7281
  application/json:
7239
7282
  schema:
7240
7283
  $ref: "#/components/schemas/ErrorMessageDto"
7241
7284
 
7242
- /workspaces/{workspaceId}/report-center/exports:
7243
- post:
7244
- tags: [WorkspaceActivity]
7245
- summary: Record a Report Center PDF export
7246
- operationId: recordReportCenterExport
7285
+ /workspaces/{workspaceId}/assessment-roi:
7286
+ get:
7287
+ tags: [Workspace]
7288
+ summary: Read the matched-period ROI from stored evidence
7289
+ operationId: getWorkspaceAssessmentRoi
7247
7290
  description: >
7248
- Workspace admin (or platform admin) records that a Report Center PDF was downloaded.
7249
- Inserts one `report.exported` activity-log row with the request body as secret-free metadata.
7250
- Does not store PDF bytes. Members who are not workspace admins receive 403.
7291
+ Derives the 90-day pre window, the 14-day transition, and the 90-day post
7292
+ window from the confirmed rollout day, then loads calendar-day coverage,
7293
+ merged pull request Ship Weight per window, and recorded USD AI spend for the
7294
+ post window. The figure is USD cost per incremental normalized Ship Weight
7295
+ (per active engineer and Mon–Fri America/New_York working day). Fails closed:
7296
+ `status` is `not_assessable` with a `reason` when a window is incomplete, cost
7297
+ is missing, non-USD, or not attributable by day, Ship Weight is unscored,
7298
+ active engineers are not countable, incremental output is not positive, or a
7299
+ confounder is flagged or cannot be checked. Cost is never estimated. Any
7300
+ workspace member may read it.
7251
7301
  parameters:
7252
7302
  - $ref: "#/components/parameters/WorkspaceId"
7253
- requestBody:
7254
- required: true
7255
- content:
7256
- application/json:
7257
- schema:
7258
- $ref: "#/components/schemas/ReportCenterExportRecordBodyDto"
7259
7303
  responses:
7260
- "201":
7261
- description: Export recorded
7262
- content:
7263
- application/json:
7264
- schema:
7265
- $ref: "#/components/schemas/ReportCenterExportRecordResponseDto"
7266
- "400":
7267
- description: Invalid body
7304
+ "200":
7305
+ description: Matched-period ROI result
7268
7306
  content:
7269
7307
  application/json:
7270
7308
  schema:
7271
- $ref: "#/components/schemas/ErrorMessageDto"
7309
+ $ref: "#/components/schemas/AssessmentRoiDto"
7272
7310
  "401":
7273
7311
  $ref: "#/components/responses/Unauthorized"
7274
7312
  "403":
@@ -7279,36 +7317,520 @@ paths:
7279
7317
  application/json:
7280
7318
  schema:
7281
7319
  $ref: "#/components/schemas/ErrorMessageDto"
7282
- "501":
7283
- description: Activity log is not supported by this backend build
7320
+ "503":
7321
+ description: Evidence storage is not configured or could not be read
7284
7322
  content:
7285
7323
  application/json:
7286
7324
  schema:
7287
7325
  $ref: "#/components/schemas/ErrorMessageDto"
7288
7326
 
7289
- /workspaces/{workspaceId}/activity:
7327
+ /workspaces/{workspaceId}/aggregate-use-consent:
7290
7328
  get:
7291
- tags: [WorkspaceActivity]
7292
- summary: Workspace activity log (paginated)
7293
- operationId: listWorkspaceActivity
7329
+ tags: [Workspace]
7330
+ summary: Read anonymized aggregate-use permission
7331
+ operationId: getAggregateUseConsent
7294
7332
  description: >
7295
- Paginated, chronological feed of workspace-scoped lifecycle events: repository add/remove/re-add
7296
- and GitHub integration add/remove. Inactive repos remain visible in this log.
7297
- Filter by a single `eventType`, a coarse `eventCategory`/`productArea`, an `eventSubcategory`
7298
- (a dotted event-type prefix that matches a whole lifecycle subgroup in one call, e.g.
7299
- `integrations.cursor.execution_key`), `actorUserId`, or date range (`since`/`until`).
7333
+ Returns whether ThinkAI may use this workspace's anonymized aggregate results in
7334
+ marketing and peer benchmarks. A workspace that never chose returns `unset`, which
7335
+ behaves as opted out (`effective: false`). The permission never covers company name,
7336
+ logo, quote, raw report, repository or person identity, or customer reference.
7337
+ Any workspace member may read it.
7300
7338
  parameters:
7301
7339
  - $ref: "#/components/parameters/WorkspaceId"
7302
- - $ref: "#/components/parameters/PaginationLimit"
7303
- - $ref: "#/components/parameters/PaginationOffset"
7304
- - $ref: "#/components/parameters/PaginationOrder"
7305
- - name: sort
7306
- in: query
7307
- required: false
7308
- description: >
7309
- Server-side sort column (the activity "5 W's"). Combined with `order` (asc/desc).
7310
- Defaults to `when` (occurredAt). `occurredAt`/`id` is always the tiebreaker.
7311
- schema:
7340
+ responses:
7341
+ "200":
7342
+ description: Current permission and the sentence reports must print
7343
+ content:
7344
+ application/json:
7345
+ schema:
7346
+ $ref: "#/components/schemas/AggregateUseConsentDto"
7347
+ "401":
7348
+ $ref: "#/components/responses/Unauthorized"
7349
+ "403":
7350
+ $ref: "#/components/responses/Forbidden"
7351
+ "404":
7352
+ description: Workspace does not exist
7353
+ content:
7354
+ application/json:
7355
+ schema:
7356
+ $ref: "#/components/schemas/ErrorMessageDto"
7357
+ put:
7358
+ tags: [Workspace]
7359
+ summary: Set anonymized aggregate-use permission
7360
+ operationId: putAggregateUseConsent
7361
+ description: >
7362
+ Workspace admins opt in or out. Every change writes one
7363
+ `workspace.aggregate_use.changed` activity-log row in the same transaction.
7364
+ Moving from `opt_in` to `opt_out` records a withdrawal and stops future extraction.
7365
+ Opt-in requires the current legal-approved notice version; while no notice is
7366
+ approved, opt-in returns `409 aggregate_use_notice_unapproved`. Opt-out is always
7367
+ accepted. A refusal does not change price, delivery, support, or assessment output.
7368
+ parameters:
7369
+ - $ref: "#/components/parameters/WorkspaceId"
7370
+ requestBody:
7371
+ required: true
7372
+ content:
7373
+ application/json:
7374
+ schema:
7375
+ $ref: "#/components/schemas/AggregateUseConsentPutDto"
7376
+ responses:
7377
+ "200":
7378
+ description: Saved permission
7379
+ content:
7380
+ application/json:
7381
+ schema:
7382
+ $ref: "#/components/schemas/AggregateUseConsentDto"
7383
+ "400":
7384
+ description: "Invalid body (`code`: `invalid_input`)"
7385
+ content:
7386
+ application/json:
7387
+ schema:
7388
+ $ref: "#/components/schemas/ErrorMessageDto"
7389
+ "401":
7390
+ $ref: "#/components/responses/Unauthorized"
7391
+ "403":
7392
+ description: "Caller is not a workspace admin (`code`: `workspace_admin_required`)"
7393
+ content:
7394
+ application/json:
7395
+ schema:
7396
+ $ref: "#/components/schemas/ErrorMessageDto"
7397
+ "404":
7398
+ description: Workspace does not exist
7399
+ content:
7400
+ application/json:
7401
+ schema:
7402
+ $ref: "#/components/schemas/ErrorMessageDto"
7403
+ "409":
7404
+ description: >
7405
+ Opt-in refused because no notice is approved (`aggregate_use_notice_unapproved`)
7406
+ or the submitted notice version is not current (`aggregate_use_notice_stale`).
7407
+ content:
7408
+ application/json:
7409
+ schema:
7410
+ $ref: "#/components/schemas/ErrorMessageDto"
7411
+
7412
+ /workspaces/{workspaceId}/report-center/exports:
7413
+ post:
7414
+ tags: [WorkspaceActivity]
7415
+ summary: Record a Report Center PDF export
7416
+ operationId: recordReportCenterExport
7417
+ description: >
7418
+ Workspace admin (or platform admin) records that a Report Center PDF was downloaded.
7419
+ Inserts one `report.exported` activity-log row with the request body as secret-free metadata.
7420
+ Does not store PDF bytes. Members who are not workspace admins receive 403.
7421
+ parameters:
7422
+ - $ref: "#/components/parameters/WorkspaceId"
7423
+ requestBody:
7424
+ required: true
7425
+ content:
7426
+ application/json:
7427
+ schema:
7428
+ $ref: "#/components/schemas/ReportCenterExportRecordBodyDto"
7429
+ responses:
7430
+ "201":
7431
+ description: Export recorded
7432
+ content:
7433
+ application/json:
7434
+ schema:
7435
+ $ref: "#/components/schemas/ReportCenterExportRecordResponseDto"
7436
+ "400":
7437
+ description: Invalid body
7438
+ content:
7439
+ application/json:
7440
+ schema:
7441
+ $ref: "#/components/schemas/ErrorMessageDto"
7442
+ "401":
7443
+ $ref: "#/components/responses/Unauthorized"
7444
+ "403":
7445
+ $ref: "#/components/responses/Forbidden"
7446
+ "404":
7447
+ description: Workspace does not exist
7448
+ content:
7449
+ application/json:
7450
+ schema:
7451
+ $ref: "#/components/schemas/ErrorMessageDto"
7452
+ "501":
7453
+ description: Activity log is not supported by this backend build
7454
+ content:
7455
+ application/json:
7456
+ schema:
7457
+ $ref: "#/components/schemas/ErrorMessageDto"
7458
+
7459
+ /workspaces/{workspaceId}/assessment-reports:
7460
+ get:
7461
+ tags: [AssessmentReports]
7462
+ summary: List stored assessment reports and their secure links
7463
+ operationId: listAssessmentReports
7464
+ description: >
7465
+ Workspace admin (or platform admin who is a workspace member). Lists stored buyer PDFs
7466
+ (metadata only, never bytes) with every secure link grant. Link tokens and token hashes are
7467
+ never returned. A hidden report stays listed: `hidden_restorable` until `restoreUntil`,
7468
+ then `restore_window_ended`. The restore window is restore capability, not erasure.
7469
+ Responses carry `Cache-Control: no-store`.
7470
+ parameters:
7471
+ - $ref: "#/components/parameters/WorkspaceId"
7472
+ responses:
7473
+ "200":
7474
+ description: Stored reports, newest first
7475
+ content:
7476
+ application/json:
7477
+ schema:
7478
+ $ref: "#/components/schemas/AssessmentReportListDto"
7479
+ "401":
7480
+ $ref: "#/components/responses/Unauthorized"
7481
+ "403":
7482
+ $ref: "#/components/responses/Forbidden"
7483
+ "404":
7484
+ description: Workspace does not exist
7485
+ content:
7486
+ application/json:
7487
+ schema:
7488
+ $ref: "#/components/schemas/ErrorMessageDto"
7489
+ "501":
7490
+ description: Stored reports are not supported by this backend build (no database)
7491
+ content:
7492
+ application/json:
7493
+ schema:
7494
+ $ref: "#/components/schemas/ErrorMessageDto"
7495
+ post:
7496
+ tags: [AssessmentReports]
7497
+ summary: Store a validated buyer PDF for secure delivery
7498
+ operationId: storeAssessmentReport
7499
+ description: >
7500
+ Workspace admin (or platform admin who is a workspace member). The client never supplies
7501
+ evidence: the server rebuilds the manifest from stored data with the same builder as
7502
+ `buildWorkspaceAssessmentManifest`, using only `build` (offer, share mode, section
7503
+ status, printed ROI), and stores the PDF only when `buyerDeliverable` is true (validated
7504
+ and internal) and the rebuilt `contentHash` equals the one sent. An external
7505
+ share-safe file is never stored here. The PDF is encrypted with AES-256-GCM and kept in
7506
+ Postgres with its sha256; it is limited to 10 MiB. Records `report.assessment_stored`
7507
+ without report content.
7508
+ parameters:
7509
+ - $ref: "#/components/parameters/WorkspaceId"
7510
+ requestBody:
7511
+ required: true
7512
+ content:
7513
+ application/json:
7514
+ schema:
7515
+ $ref: "#/components/schemas/StoreAssessmentReportBodyDto"
7516
+ responses:
7517
+ "201":
7518
+ description: Report stored
7519
+ content:
7520
+ application/json:
7521
+ schema:
7522
+ $ref: "#/components/schemas/AssessmentReportDto"
7523
+ "400":
7524
+ description: Body is not valid, or the file is not a PDF or is too large
7525
+ content:
7526
+ application/json:
7527
+ schema:
7528
+ $ref: "#/components/schemas/ErrorMessageDto"
7529
+ "401":
7530
+ $ref: "#/components/responses/Unauthorized"
7531
+ "403":
7532
+ $ref: "#/components/responses/Forbidden"
7533
+ "404":
7534
+ description: Workspace does not exist
7535
+ content:
7536
+ application/json:
7537
+ schema:
7538
+ $ref: "#/components/schemas/ErrorMessageDto"
7539
+ "422":
7540
+ description: >
7541
+ `not_buyer_deliverable` (the rebuilt manifest did not validate as an internal buyer
7542
+ file; includes `reasons`) or `manifest_changed` (stored evidence changed since the PDF
7543
+ was built, so `contentHash` differs; export again).
7544
+ content:
7545
+ application/json:
7546
+ schema:
7547
+ $ref: "#/components/schemas/StoreAssessmentReportRejectedDto"
7548
+ "503":
7549
+ description: Stored evidence could not be loaded to rebuild the manifest.
7550
+ content:
7551
+ application/json:
7552
+ schema:
7553
+ $ref: "#/components/schemas/ErrorMessageDto"
7554
+ "501":
7555
+ description: Stored reports are not supported by this backend build (no database)
7556
+ content:
7557
+ application/json:
7558
+ schema:
7559
+ $ref: "#/components/schemas/ErrorMessageDto"
7560
+
7561
+ /workspaces/{workspaceId}/assessment-reports/{reportId}/links:
7562
+ post:
7563
+ tags: [AssessmentReports]
7564
+ summary: Create a secure link and email it to a workspace member
7565
+ operationId: createAssessmentReportLink
7566
+ description: >
7567
+ Workspace admin (or platform admin who is a workspace member). Creates one link grant for a
7568
+ current workspace member and emails them the link only: generic text and
7569
+ `{siteUrl}/reports/secure?t=<opaque token>`. The email carries no PDF attachment, report
7570
+ name, metrics, repository, or person names. Only the sha256 of the token is stored and the
7571
+ token is never returned. Expiry defaults to 14 days and can be shorter. When SMTP is not
7572
+ configured the grant still stands and `email` says why it was skipped. Hidden reports
7573
+ cannot be shared (`409 report_hidden`).
7574
+ parameters:
7575
+ - $ref: "#/components/parameters/WorkspaceId"
7576
+ - $ref: "#/components/parameters/AssessmentReportId"
7577
+ requestBody:
7578
+ required: true
7579
+ content:
7580
+ application/json:
7581
+ schema:
7582
+ $ref: "#/components/schemas/CreateAssessmentReportLinkBodyDto"
7583
+ responses:
7584
+ "201":
7585
+ description: Link created
7586
+ content:
7587
+ application/json:
7588
+ schema:
7589
+ $ref: "#/components/schemas/CreateAssessmentReportLinkResponseDto"
7590
+ "400":
7591
+ description: Body is not valid
7592
+ content:
7593
+ application/json:
7594
+ schema:
7595
+ $ref: "#/components/schemas/ErrorMessageDto"
7596
+ "401":
7597
+ $ref: "#/components/responses/Unauthorized"
7598
+ "403":
7599
+ $ref: "#/components/responses/Forbidden"
7600
+ "404":
7601
+ description: Workspace or report not found (`report_not_found`)
7602
+ content:
7603
+ application/json:
7604
+ schema:
7605
+ $ref: "#/components/schemas/ErrorMessageDto"
7606
+ "409":
7607
+ description: The report is hidden (`report_hidden`)
7608
+ content:
7609
+ application/json:
7610
+ schema:
7611
+ $ref: "#/components/schemas/ErrorMessageDto"
7612
+ "422":
7613
+ description: The recipient is not a current workspace member (`recipient_not_member`)
7614
+ content:
7615
+ application/json:
7616
+ schema:
7617
+ $ref: "#/components/schemas/ErrorMessageDto"
7618
+ "501":
7619
+ description: Stored reports or member listing are not supported by this backend build
7620
+ content:
7621
+ application/json:
7622
+ schema:
7623
+ $ref: "#/components/schemas/ErrorMessageDto"
7624
+
7625
+ /workspaces/{workspaceId}/assessment-reports/{reportId}/links/{grantId}/revoke:
7626
+ post:
7627
+ tags: [AssessmentReports]
7628
+ summary: Revoke a secure link
7629
+ operationId: revokeAssessmentReportLink
7630
+ description: >
7631
+ Workspace admin (or platform admin who is a workspace member). The link stops working
7632
+ immediately. Idempotent: revoking again keeps the first `revokedAt`. Revoking a link does
7633
+ not affect a file the recipient already downloaded. Records `report.assessment_link_revoked`.
7634
+ parameters:
7635
+ - $ref: "#/components/parameters/WorkspaceId"
7636
+ - $ref: "#/components/parameters/AssessmentReportId"
7637
+ - name: grantId
7638
+ in: path
7639
+ required: true
7640
+ schema:
7641
+ type: string
7642
+ format: uuid
7643
+ responses:
7644
+ "200":
7645
+ description: Revoked link
7646
+ content:
7647
+ application/json:
7648
+ schema:
7649
+ $ref: "#/components/schemas/AssessmentReportLinkDto"
7650
+ "401":
7651
+ $ref: "#/components/responses/Unauthorized"
7652
+ "403":
7653
+ $ref: "#/components/responses/Forbidden"
7654
+ "404":
7655
+ description: Workspace, report, or link not found
7656
+ content:
7657
+ application/json:
7658
+ schema:
7659
+ $ref: "#/components/schemas/ErrorMessageDto"
7660
+ "501":
7661
+ description: Stored reports are not supported by this backend build (no database)
7662
+ content:
7663
+ application/json:
7664
+ schema:
7665
+ $ref: "#/components/schemas/ErrorMessageDto"
7666
+
7667
+ /workspaces/{workspaceId}/assessment-reports/{reportId}/hide:
7668
+ post:
7669
+ tags: [AssessmentReports]
7670
+ summary: Hide a stored report
7671
+ operationId: hideAssessmentReport
7672
+ description: >
7673
+ Workspace admin (or platform admin who is a workspace member). Same semantics as workspace
7674
+ delete: the report is hidden immediately and every link stops downloading. It can be
7675
+ restored for 30 days (`restoreUntil`). That window is restore capability, not erasure; no
7676
+ backup purge date is promised, and a file the buyer already downloaded stays with them.
7677
+ Idempotent. Records `report.assessment_hidden`.
7678
+ parameters:
7679
+ - $ref: "#/components/parameters/WorkspaceId"
7680
+ - $ref: "#/components/parameters/AssessmentReportId"
7681
+ responses:
7682
+ "200":
7683
+ description: Hidden report (`links` is empty in this response; list again for links)
7684
+ content:
7685
+ application/json:
7686
+ schema:
7687
+ $ref: "#/components/schemas/AssessmentReportDto"
7688
+ "401":
7689
+ $ref: "#/components/responses/Unauthorized"
7690
+ "403":
7691
+ $ref: "#/components/responses/Forbidden"
7692
+ "404":
7693
+ description: Workspace or report not found
7694
+ content:
7695
+ application/json:
7696
+ schema:
7697
+ $ref: "#/components/schemas/ErrorMessageDto"
7698
+ "501":
7699
+ description: Stored reports are not supported by this backend build (no database)
7700
+ content:
7701
+ application/json:
7702
+ schema:
7703
+ $ref: "#/components/schemas/ErrorMessageDto"
7704
+
7705
+ /workspaces/{workspaceId}/assessment-reports/{reportId}/restore:
7706
+ post:
7707
+ tags: [AssessmentReports]
7708
+ summary: Restore a hidden report within the 30-day window
7709
+ operationId: restoreAssessmentReport
7710
+ description: >
7711
+ Workspace admin (or platform admin who is a workspace member). Allowed only before
7712
+ `restoreUntil`; afterwards it fails with `409 restore_window_ended`. Links that were not
7713
+ revoked or expired work again after restore. Records `report.assessment_restored`.
7714
+ parameters:
7715
+ - $ref: "#/components/parameters/WorkspaceId"
7716
+ - $ref: "#/components/parameters/AssessmentReportId"
7717
+ responses:
7718
+ "200":
7719
+ description: Restored report (`links` is empty in this response; list again for links)
7720
+ content:
7721
+ application/json:
7722
+ schema:
7723
+ $ref: "#/components/schemas/AssessmentReportDto"
7724
+ "401":
7725
+ $ref: "#/components/responses/Unauthorized"
7726
+ "403":
7727
+ $ref: "#/components/responses/Forbidden"
7728
+ "404":
7729
+ description: Workspace or report not found
7730
+ content:
7731
+ application/json:
7732
+ schema:
7733
+ $ref: "#/components/schemas/ErrorMessageDto"
7734
+ "409":
7735
+ description: "`not_hidden` or `restore_window_ended`"
7736
+ content:
7737
+ application/json:
7738
+ schema:
7739
+ $ref: "#/components/schemas/ErrorMessageDto"
7740
+ "501":
7741
+ description: Stored reports are not supported by this backend build (no database)
7742
+ content:
7743
+ application/json:
7744
+ schema:
7745
+ $ref: "#/components/schemas/ErrorMessageDto"
7746
+
7747
+ /assessment-reports/secure:
7748
+ get:
7749
+ tags: [AssessmentReports]
7750
+ summary: Download a stored report through a secure link
7751
+ operationId: downloadAssessmentReport
7752
+ description: >
7753
+ Bearer JWT required. The caller must be the link recipient and a current member of the
7754
+ report's workspace. Access follows `decideReportAccess`: `leaked_token`, `removed_member`,
7755
+ and `wrong_workspace` answer 403; `expired` and `revoked` answer 410; `invalid_grant`
7756
+ answers 404. A hidden report answers `410 report_hidden`. Every response carries
7757
+ `Cache-Control: no-store`. Records `report.assessment_downloaded` without report content.
7758
+ parameters:
7759
+ - name: t
7760
+ in: query
7761
+ required: true
7762
+ description: Opaque link token from the email (`secureLinkQuery`).
7763
+ schema:
7764
+ type: string
7765
+ responses:
7766
+ "200":
7767
+ description: The decrypted PDF
7768
+ headers:
7769
+ Cache-Control:
7770
+ schema:
7771
+ type: string
7772
+ enum: [no-store]
7773
+ content:
7774
+ application/pdf:
7775
+ schema:
7776
+ type: string
7777
+ format: binary
7778
+ "401":
7779
+ $ref: "#/components/responses/Unauthorized"
7780
+ "403":
7781
+ description: "`leaked_token`, `removed_member`, or `wrong_workspace`"
7782
+ content:
7783
+ application/json:
7784
+ schema:
7785
+ $ref: "#/components/schemas/AssessmentReportAccessErrorDto"
7786
+ "404":
7787
+ description: "`invalid_grant`"
7788
+ content:
7789
+ application/json:
7790
+ schema:
7791
+ $ref: "#/components/schemas/AssessmentReportAccessErrorDto"
7792
+ "410":
7793
+ description: "`expired`, `revoked`, or `report_hidden`"
7794
+ content:
7795
+ application/json:
7796
+ schema:
7797
+ $ref: "#/components/schemas/AssessmentReportAccessErrorDto"
7798
+ "500":
7799
+ description: The stored report could not be decrypted (`report_unreadable`)
7800
+ content:
7801
+ application/json:
7802
+ schema:
7803
+ $ref: "#/components/schemas/ErrorMessageDto"
7804
+ "501":
7805
+ description: Stored reports are not supported by this backend build (no database)
7806
+ content:
7807
+ application/json:
7808
+ schema:
7809
+ $ref: "#/components/schemas/ErrorMessageDto"
7810
+
7811
+ /workspaces/{workspaceId}/activity:
7812
+ get:
7813
+ tags: [WorkspaceActivity]
7814
+ summary: Workspace activity log (paginated)
7815
+ operationId: listWorkspaceActivity
7816
+ description: >
7817
+ Paginated, chronological feed of workspace-scoped lifecycle events: repository add/remove/re-add
7818
+ and GitHub integration add/remove. Inactive repos remain visible in this log.
7819
+ Filter by a single `eventType`, a coarse `eventCategory`/`productArea`, an `eventSubcategory`
7820
+ (a dotted event-type prefix that matches a whole lifecycle subgroup in one call, e.g.
7821
+ `integrations.cursor.execution_key`), `actorUserId`, or date range (`since`/`until`).
7822
+ parameters:
7823
+ - $ref: "#/components/parameters/WorkspaceId"
7824
+ - $ref: "#/components/parameters/PaginationLimit"
7825
+ - $ref: "#/components/parameters/PaginationOffset"
7826
+ - $ref: "#/components/parameters/PaginationOrder"
7827
+ - name: sort
7828
+ in: query
7829
+ required: false
7830
+ description: >
7831
+ Server-side sort column (the activity "5 W's"). Combined with `order` (asc/desc).
7832
+ Defaults to `when` (occurredAt). `occurredAt`/`id` is always the tiebreaker.
7833
+ schema:
7312
7834
  type: string
7313
7835
  enum: [when, who, what, why, where]
7314
7836
  - name: productArea
@@ -10989,7 +11511,7 @@ paths:
10989
11511
  derives teams client-side and saves the org structure via `POST /workspaces/{workspaceId}/org-chart`.
10990
11512
  Required columns: employee_id, name, email, title, department, team, manager_id, location.
10991
11513
  Optional: github_username (GitHub login for productivity ingest), bitbucket_nickname,
10992
- bitbucket_uuid, alternate_emails (comma-separated alternate work emails for AI tool spend
11514
+ bitbucket_uuid, gitlab_username (GitLab.com or self-managed username), alternate_emails (comma-separated alternate work emails for AI tool spend
10993
11515
  attribution). Saving the SPA snapshot also materializes a scoring `org_chart` tree for
10994
11516
  team/person metrics when import members are present. Parse-only responses may include
10995
11517
  duplicateEmailWarnings, identityEmailPreview (cross-check against connected AI tool seat
@@ -12005,6 +12527,106 @@ paths:
12005
12527
  "403":
12006
12528
  $ref: "#/components/responses/Forbidden"
12007
12529
 
12530
+ /workspaces/{workspaceId}/sources/discover:
12531
+ post:
12532
+ tags: [Workspace]
12533
+ summary: List repositories a GitLab or Bitbucket source can read (does not persist)
12534
+ description: >
12535
+ Lists projects (GitLab.com, self-managed GitLab) or repositories (Bitbucket Cloud,
12536
+ Bitbucket Data Center) for repository selection. Nothing is stored. Self-managed and
12537
+ Data Center `baseUrl` must be `https`, public, and without credentials; private,
12538
+ loopback, and link-local hosts are rejected before any request. `env:` token
12539
+ references are rejected for self-hosted providers and never resolved from the request
12540
+ for cloud providers. A blank or `***` token reuses the stored source with the same
12541
+ provider, host, and account, and requires the editor or admin role. The list covers
12542
+ the whole account (group or workspace/project key), not only `projects`/`repos`.
12543
+ `repoCap` is the standard repository cap (50); ingest refuses to auto-select more
12544
+ than that. Tokens are never returned.
12545
+ operationId: postWorkspaceVcsRepoDiscovery
12546
+ parameters:
12547
+ - $ref: "#/components/parameters/WorkspaceId"
12548
+ requestBody:
12549
+ required: true
12550
+ content:
12551
+ application/json:
12552
+ schema:
12553
+ $ref: "#/components/schemas/GitVcsSourceDto"
12554
+ examples:
12555
+ gitlabSelfManaged:
12556
+ summary: Self-managed GitLab group
12557
+ value:
12558
+ type: gitlab
12559
+ deployment: self_managed
12560
+ baseUrl: https://gitlab.example.com
12561
+ group: platform
12562
+ token: glpat-example
12563
+ bitbucketDataCenter:
12564
+ summary: Bitbucket Data Center project
12565
+ value:
12566
+ type: bitbucket
12567
+ deployment: data_center
12568
+ baseUrl: https://bitbucket.example.com
12569
+ workspace: PLAT
12570
+ token: "***"
12571
+ responses:
12572
+ "200":
12573
+ description: Repositories the source can read
12574
+ content:
12575
+ application/json:
12576
+ schema:
12577
+ $ref: "#/components/schemas/VcsRepoDiscoveryResponseDto"
12578
+ "400":
12579
+ description: >
12580
+ Invalid source (`error` is a code such as `base_url_required`, `base_url_invalid`,
12581
+ `base_url_not_https`, `base_url_has_credentials`, `base_url_has_path`,
12582
+ `base_url_host_blocked`, `env_ref_not_allowed`, `account_missing`, `token_missing`,
12583
+ `unsupported_type`).
12584
+ content:
12585
+ application/json:
12586
+ schema:
12587
+ $ref: "#/components/schemas/ErrorMessageDto"
12588
+ "401":
12589
+ $ref: "#/components/responses/Unauthorized"
12590
+ "403":
12591
+ $ref: "#/components/responses/Forbidden"
12592
+ "502":
12593
+ description: >
12594
+ The provider rejected or failed the listing (`error` is `unauthorized`, `forbidden`,
12595
+ `not_found`, `rate_limited`, or `discovery_failed`).
12596
+ content:
12597
+ application/json:
12598
+ schema:
12599
+ $ref: "#/components/schemas/ErrorMessageDto"
12600
+
12601
+ /workspaces/{workspaceId}/vcs-provider-coverage:
12602
+ get:
12603
+ tags: [Workspace]
12604
+ summary: GitLab and Bitbucket Data Coverage freshness (server-computed)
12605
+ operationId: getWorkspaceVcsProviderCoverage
12606
+ description: >
12607
+ One row per configured GitLab or Bitbucket source. Freshness is computed on the server
12608
+ from batch-written sync rows and succeeded readiness runs; no request field can mark a
12609
+ row fresh. Git is fresh when the last complete ingest finished within
12610
+ `gitFreshnessHours` (24) and no error was recorded after it. A readiness scan is fresh
12611
+ within `readinessScanFreshnessDays` (30, America/New_York calendar days); `scansFresh`
12612
+ requires every in-scope repository to have a fresh scan. A source with an invalid base
12613
+ URL or missing account is `misconfigured`. Requires workspace membership.
12614
+ parameters:
12615
+ - $ref: "#/components/parameters/WorkspaceId"
12616
+ responses:
12617
+ "200":
12618
+ description: Server-computed provider coverage
12619
+ content:
12620
+ application/json:
12621
+ schema:
12622
+ $ref: "#/components/schemas/VcsProviderCoverageDto"
12623
+ "401":
12624
+ $ref: "#/components/responses/Unauthorized"
12625
+ "403":
12626
+ $ref: "#/components/responses/Forbidden"
12627
+ "501":
12628
+ description: Store does not support provider coverage
12629
+
12008
12630
  /workspaces/{workspaceId}/ingest/productivity/repos/{slug}/retry:
12009
12631
  post:
12010
12632
  tags: [Workspace]
@@ -12915,6 +13537,14 @@ components:
12915
13537
  required: true
12916
13538
  schema:
12917
13539
  type: string
13540
+ AssessmentReportId:
13541
+ name: reportId
13542
+ in: path
13543
+ required: true
13544
+ schema:
13545
+ type: string
13546
+ format: uuid
13547
+ description: Stored assessment report id (`tai_workspace_assessment_reports.id`).
12918
13548
  EvalSuiteId:
12919
13549
  name: suiteId
12920
13550
  in: path
@@ -13337,7 +13967,174 @@ components:
13337
13967
  example:
13338
13968
  error: zenhr_sync_in_progress
13339
13969
 
13340
- schemas:
13970
+ schemas:
13971
+ AssessmentManifestValidationBodyDto:
13972
+ description: Body of runAssessmentManifestValidation; buildWorkspaceAssessmentManifest returns the server-built object as `validation`.
13973
+ type: object
13974
+ required:
13975
+ - manifest
13976
+ - workspaceId
13977
+ - expectedWorkspaceId
13978
+ - expectedRepositoryIds
13979
+ - soldWindowComplete
13980
+ - premiumAiAssessable
13981
+ - namedPersonSectionIds
13982
+ - tolerances
13983
+ properties:
13984
+ manifest:
13985
+ type: object
13986
+ required:
13987
+ - offerId
13988
+ - dictionaryVersion
13989
+ - validation
13990
+ - shareMode
13991
+ - watermark
13992
+ - scope
13993
+ - windows
13994
+ - sources
13995
+ - consent
13996
+ - sections
13997
+ properties:
13998
+ offerId:
13999
+ type: string
14000
+ enum: [engineering_readiness_baseline, agentic_ai_roi]
14001
+ dictionaryVersion:
14002
+ type: string
14003
+ validation:
14004
+ type: string
14005
+ enum: [validated, not_validated]
14006
+ shareMode:
14007
+ type: string
14008
+ enum: [internal, external]
14009
+ watermark:
14010
+ type: string
14011
+ nullable: true
14012
+ enum: ["NOT VALIDATED", "SAMPLE · SYNTHETIC DATA"]
14013
+ scope:
14014
+ type: object
14015
+ required: [repositories, excluded]
14016
+ properties:
14017
+ repositories:
14018
+ type: array
14019
+ items:
14020
+ type: object
14021
+ required: [id, name]
14022
+ properties:
14023
+ id: { type: string }
14024
+ name: { type: string }
14025
+ excluded:
14026
+ type: array
14027
+ items:
14028
+ type: object
14029
+ required: [id, name, reason]
14030
+ properties:
14031
+ id: { type: string }
14032
+ name: { type: string }
14033
+ reason: { type: string }
14034
+ windows:
14035
+ type: object
14036
+ required: [pre, post, transitionDays]
14037
+ properties:
14038
+ pre:
14039
+ type: object
14040
+ nullable: true
14041
+ required: [start, end]
14042
+ properties:
14043
+ start: { type: string }
14044
+ end: { type: string }
14045
+ post:
14046
+ type: object
14047
+ nullable: true
14048
+ required: [start, end]
14049
+ properties:
14050
+ start: { type: string }
14051
+ end: { type: string }
14052
+ transitionDays:
14053
+ type: integer
14054
+ sources:
14055
+ type: array
14056
+ items:
14057
+ type: object
14058
+ required: [provider, status, freshAt]
14059
+ properties:
14060
+ provider: { type: string }
14061
+ status: { type: string }
14062
+ freshAt: { type: string, nullable: true }
14063
+ consent:
14064
+ type: object
14065
+ required: [aggregateUse, recordedAt]
14066
+ properties:
14067
+ aggregateUse:
14068
+ type: string
14069
+ enum: [opt_in, opt_out, unset]
14070
+ recordedAt:
14071
+ type: string
14072
+ nullable: true
14073
+ sections:
14074
+ type: array
14075
+ items:
14076
+ type: object
14077
+ required: [id, status]
14078
+ properties:
14079
+ id: { type: string }
14080
+ status:
14081
+ type: string
14082
+ enum: [ok, failed, omitted]
14083
+ reason: { type: string }
14084
+ workspaceId:
14085
+ type: string
14086
+ expectedWorkspaceId:
14087
+ type: string
14088
+ expectedRepositoryIds:
14089
+ type: array
14090
+ items:
14091
+ type: string
14092
+ soldWindowComplete:
14093
+ type: boolean
14094
+ premiumAiAssessable:
14095
+ type: boolean
14096
+ namedPersonSectionIds:
14097
+ type: array
14098
+ items:
14099
+ type: string
14100
+ tolerances:
14101
+ type: array
14102
+ items:
14103
+ type: object
14104
+ required: [id, status]
14105
+ properties:
14106
+ id:
14107
+ type: string
14108
+ status:
14109
+ type: string
14110
+ enum: [unset, exceeded, within]
14111
+
14112
+ AssessmentManifestValidationResultDto:
14113
+ type: object
14114
+ required: [buyerDeliverable, validation, watermark, reasons, contentHash]
14115
+ properties:
14116
+ buyerDeliverable:
14117
+ type: boolean
14118
+ validation:
14119
+ type: string
14120
+ enum: [validated, not_validated]
14121
+ watermark:
14122
+ type: string
14123
+ nullable: true
14124
+ enum: ["NOT VALIDATED"]
14125
+ reasons:
14126
+ type: array
14127
+ items:
14128
+ type: object
14129
+ required: [code, message]
14130
+ properties:
14131
+ code:
14132
+ type: string
14133
+ message:
14134
+ type: string
14135
+ contentHash:
14136
+ type: string
14137
+
13341
14138
  TeamsInstallUrlDto:
13342
14139
  type: object
13343
14140
  required: [url]
@@ -13678,6 +14475,9 @@ components:
13678
14475
  bitbucketUuid:
13679
14476
  type: string
13680
14477
  description: Bitbucket account UUID (e.g. "{aeefb6b0-...}") to attribute merged PRs and reviews to this person in productivity ingest. Preferred over bitbucketNickname — Bitbucket PR payloads expose a stable uuid but not nickname. Resolves to the same person_id (email) as githubUsername.
14478
+ gitlabUsername:
14479
+ type: string
14480
+ description: GitLab username (no @; GitLab.com or self-managed) to map merged merge requests and commits to this person in productivity ingest and the assessment active-engineer count. Stored lowercased. Resolves to the same person_id (email) as githubUsername.
13681
14481
  alternateEmails:
13682
14482
  type: string
13683
14483
  description: Optional comma-separated work emails that map to the same person for AI tool spend/usage (mirrors multi-login github_username). Primary email remains canonical person_id.
@@ -13752,6 +14552,8 @@ components:
13752
14552
  type: string
13753
14553
  bitbucketUuid:
13754
14554
  type: string
14555
+ gitlabUsername:
14556
+ type: string
13755
14557
  alternateEmails:
13756
14558
  type: string
13757
14559
  description: Comma-separated alternate work emails for AI tool attribution.
@@ -14210,6 +15012,74 @@ components:
14210
15012
  type: string
14211
15013
  description: New Relic user name when known from the last successful verify.
14212
15014
 
15015
+ DatadogSourceDto:
15016
+ type: object
15017
+ description: >
15018
+ Datadog monitoring source for `PUT /workspaces/{workspaceId}/sources` and
15019
+ `POST /workspaces/{workspaceId}/sources/test`. Requires an API key, an Application
15020
+ key, and a site host suffix (US1/US3/US5/EU/AP1). Raw keys are write-only.
15021
+ required: [type, apiKey, appKey, site]
15022
+ additionalProperties: false
15023
+ properties:
15024
+ type:
15025
+ type: string
15026
+ enum: [datadog]
15027
+ apiKey:
15028
+ type: string
15029
+ minLength: 1
15030
+ maxLength: 512
15031
+ description: Datadog API key (write-only on PUT; encrypted at rest).
15032
+ appKey:
15033
+ type: string
15034
+ minLength: 1
15035
+ maxLength: 512
15036
+ description: Datadog Application key (write-only on PUT; encrypted at rest).
15037
+ site:
15038
+ type: string
15039
+ enum:
15040
+ - datadoghq.com
15041
+ - us3.datadoghq.com
15042
+ - us5.datadoghq.com
15043
+ - datadoghq.eu
15044
+ - ap1.datadoghq.com
15045
+ description: >
15046
+ Datadog site host suffix. Maps to API host `api.{site}`
15047
+ (US1 `datadoghq.com`, US3 `us3.datadoghq.com`, US5 `us5.datadoghq.com`,
15048
+ EU `datadoghq.eu`, AP1 `ap1.datadoghq.com`). Gov/custom sites are out of scope.
15049
+ orgName:
15050
+ type: string
15051
+ description: Optional org display name returned from a successful connection test.
15052
+
15053
+ DatadogSourceConfigDto:
15054
+ type: object
15055
+ description: >
15056
+ Redacted Datadog source returned by `GET /workspaces/{workspaceId}/config`.
15057
+ Raw `apiKey` and `appKey` are never returned.
15058
+ required: [type, hasApiKey, hasAppKey, site]
15059
+ additionalProperties: false
15060
+ properties:
15061
+ type:
15062
+ type: string
15063
+ enum: [datadog]
15064
+ hasApiKey:
15065
+ type: boolean
15066
+ description: Whether a Datadog API key is stored for this workspace.
15067
+ hasAppKey:
15068
+ type: boolean
15069
+ description: Whether a Datadog Application key is stored for this workspace.
15070
+ site:
15071
+ type: string
15072
+ enum:
15073
+ - datadoghq.com
15074
+ - us3.datadoghq.com
15075
+ - us5.datadoghq.com
15076
+ - datadoghq.eu
15077
+ - ap1.datadoghq.com
15078
+ description: Configured Datadog site host suffix.
15079
+ orgName:
15080
+ type: string
15081
+ description: Org display name when known from the last successful verify.
15082
+
14213
15083
  PrometheusSourceDto:
14214
15084
  type: object
14215
15085
  description: >
@@ -14765,6 +15635,7 @@ components:
14765
15635
  - $ref: "#/components/schemas/ClaudeSourceConfigDto"
14766
15636
  - $ref: "#/components/schemas/CodeIntelligenceSourceConfigDto"
14767
15637
  - $ref: "#/components/schemas/NewRelicSourceConfigDto"
15638
+ - $ref: "#/components/schemas/DatadogSourceConfigDto"
14768
15639
  - $ref: "#/components/schemas/PrometheusSourceConfigDto"
14769
15640
  - $ref: "#/components/schemas/GrafanaSourceConfigDto"
14770
15641
  - $ref: "#/components/schemas/SonarQubeSourceConfigDto"
@@ -15025,7 +15896,7 @@ components:
15025
15896
  description: Present for New Relic or Jira verify success — resolved account ID.
15026
15897
  orgName:
15027
15898
  type: string
15028
- description: Present for Grafana verify success — resolved organization name.
15899
+ description: Present for Grafana or Datadog verify success — resolved organization name.
15029
15900
  clusterName:
15030
15901
  type: string
15031
15902
  description: Present for Prometheus verify success — Alertmanager cluster name.
@@ -19072,17 +19943,199 @@ components:
19072
19943
  - platform-governance
19073
19944
  - delivery
19074
19945
 
19946
+ ExternalVcsProviderDto:
19947
+ type: string
19948
+ enum: [gitlab_cloud, gitlab_self_managed, bitbucket_cloud, bitbucket_server]
19949
+ description: >
19950
+ `bitbucket_server` is Bitbucket Data Center. Self-hosted providers
19951
+ (`gitlab_self_managed`, `bitbucket_server`) always carry `hostBaseUrl`.
19952
+
19953
+ GitVcsSourceDto:
19954
+ type: object
19955
+ description: >
19956
+ GitLab or Bitbucket tenant source entry. GitLab: optional `group` (full path) and
19957
+ `projects` (`group/sub/project`). Bitbucket: `workspace` (Cloud workspace or Data Center
19958
+ project key) and optional `repos` (repository slugs). `deployment` selects self-hosted;
19959
+ it then requires `baseUrl`.
19960
+ required: [type]
19961
+ additionalProperties: true
19962
+ properties:
19963
+ type:
19964
+ type: string
19965
+ enum: [gitlab, bitbucket]
19966
+ deployment:
19967
+ type: string
19968
+ enum: [cloud, self_managed, data_center]
19969
+ baseUrl:
19970
+ type: string
19971
+ maxLength: 512
19972
+ group:
19973
+ type: string
19974
+ maxLength: 512
19975
+ projects:
19976
+ type: array
19977
+ items:
19978
+ type: string
19979
+ maxLength: 512
19980
+ workspace:
19981
+ type: string
19982
+ maxLength: 256
19983
+ repos:
19984
+ type: array
19985
+ items:
19986
+ type: string
19987
+ maxLength: 256
19988
+ token:
19989
+ type: string
19990
+ maxLength: 4096
19991
+ description: Personal / project / HTTP access token, or `***` to reuse the stored token.
19992
+
19993
+ VcsDiscoveredRepoDto:
19994
+ type: object
19995
+ required: [slug, name, defaultBranch, webUrl]
19996
+ properties:
19997
+ slug:
19998
+ type: string
19999
+ description: "`group/sub/project` (GitLab) or `workspace/repo` / `PROJECT/repo` (Bitbucket)"
20000
+ name:
20001
+ type: string
20002
+ defaultBranch:
20003
+ type: string
20004
+ nullable: true
20005
+ webUrl:
20006
+ type: string
20007
+ nullable: true
20008
+
20009
+ VcsRepoDiscoveryResponseDto:
20010
+ type: object
20011
+ required: [provider, hostBaseUrl, account, truncated, repoCap, repositories]
20012
+ properties:
20013
+ provider:
20014
+ $ref: "#/components/schemas/ExternalVcsProviderDto"
20015
+ hostBaseUrl:
20016
+ type: string
20017
+ nullable: true
20018
+ account:
20019
+ type: string
20020
+ description: GitLab group path (empty = token membership), Bitbucket workspace, or project key
20021
+ truncated:
20022
+ type: boolean
20023
+ description: True when the provider page limit was reached; the list is incomplete.
20024
+ repoCap:
20025
+ type: integer
20026
+ repositories:
20027
+ type: array
20028
+ items:
20029
+ $ref: "#/components/schemas/VcsDiscoveredRepoDto"
20030
+
20031
+ VcsProviderCoverageRowDto:
20032
+ type: object
20033
+ required:
20034
+ - provider
20035
+ - hostBaseUrl
20036
+ - account
20037
+ - selfHosted
20038
+ - state
20039
+ - isFresh
20040
+ - lastIngestSuccessAt
20041
+ - lastErrorKind
20042
+ - lastDiscoveryAt
20043
+ - discoveredRepoCount
20044
+ - staleForMs
20045
+ - repoCount
20046
+ - scannedRepoCount
20047
+ - freshScanRepoCount
20048
+ - scansFresh
20049
+ - mergedHistorySince
20050
+ - commitHistorySince
20051
+ properties:
20052
+ provider:
20053
+ $ref: "#/components/schemas/ExternalVcsProviderDto"
20054
+ hostBaseUrl:
20055
+ type: string
20056
+ nullable: true
20057
+ account:
20058
+ type: string
20059
+ selfHosted:
20060
+ type: boolean
20061
+ state:
20062
+ type: string
20063
+ enum: [fresh, stale, never_synced, error, misconfigured]
20064
+ isFresh:
20065
+ type: boolean
20066
+ description: Server-computed; true only when `state` is `fresh`.
20067
+ lastIngestSuccessAt:
20068
+ type: string
20069
+ format: date-time
20070
+ nullable: true
20071
+ lastErrorKind:
20072
+ type: string
20073
+ nullable: true
20074
+ lastDiscoveryAt:
20075
+ type: string
20076
+ format: date-time
20077
+ nullable: true
20078
+ discoveredRepoCount:
20079
+ type: integer
20080
+ nullable: true
20081
+ staleForMs:
20082
+ type: integer
20083
+ nullable: true
20084
+ description: Milliseconds past the Git freshness limit when `state` is `stale`.
20085
+ repoCount:
20086
+ type: integer
20087
+ description: In-scope repositories stored for this source.
20088
+ scannedRepoCount:
20089
+ type: integer
20090
+ freshScanRepoCount:
20091
+ type: integer
20092
+ scansFresh:
20093
+ type: boolean
20094
+ mergedHistorySince:
20095
+ type: string
20096
+ format: date-time
20097
+ nullable: true
20098
+ description: >-
20099
+ Earliest merge time reached by the assessment merged-request history backfill for this
20100
+ source; null when no backfill window has been written.
20101
+ commitHistorySince:
20102
+ type: string
20103
+ format: date-time
20104
+ nullable: true
20105
+ description: >-
20106
+ Instant from which every in-scope repository of this source has contiguous
20107
+ default-branch commit-author coverage (latest per-repo coverage start); null when any
20108
+ repository has no coverage.
20109
+
20110
+ VcsProviderCoverageDto:
20111
+ type: object
20112
+ required: [generatedAt, gitFreshnessHours, readinessScanFreshnessDays, sources]
20113
+ properties:
20114
+ generatedAt:
20115
+ type: string
20116
+ format: date-time
20117
+ gitFreshnessHours:
20118
+ type: integer
20119
+ readinessScanFreshnessDays:
20120
+ type: integer
20121
+ sources:
20122
+ type: array
20123
+ items:
20124
+ $ref: "#/components/schemas/VcsProviderCoverageRowDto"
20125
+
19075
20126
  RepoProviderDto:
19076
20127
  type: object
19077
20128
  required: [kind, slug, webUrl]
19078
20129
  properties:
19079
20130
  kind:
19080
20131
  type: string
19081
- enum: [github, bitbucket_cloud, bitbucket_server]
20132
+ enum: [github, bitbucket_cloud, bitbucket_server, gitlab_cloud, gitlab_self_managed]
19082
20133
  slug:
19083
20134
  type: string
19084
20135
  maxLength: 256
19085
- description: owner/repo (GitHub), workspace/repo_slug (Bitbucket Cloud), or PROJECT_KEY/repo_slug (Bitbucket Server)
20136
+ description: >
20137
+ owner/repo (GitHub), workspace/repo_slug (Bitbucket Cloud), PROJECT_KEY/repo_slug
20138
+ (Bitbucket Server / Data Center), or group/sub/project (GitLab)
19086
20139
  webUrl:
19087
20140
  type: string
19088
20141
  format: uri
@@ -19100,7 +20153,9 @@ components:
19100
20153
  type: string
19101
20154
  format: uri
19102
20155
  nullable: true
19103
- description: Required for bitbucket_server (self-hosted); null for cloud providers
20156
+ description: >
20157
+ Required for bitbucket_server and gitlab_self_managed (self-hosted); null for cloud
20158
+ providers
19104
20159
 
19105
20160
  ReadinessAnalyzeBodyDto:
19106
20161
  type: object
@@ -22277,29 +23332,309 @@ components:
22277
23332
  - integrations.zelt.added
22278
23333
  - integrations.zelt.removed
22279
23334
  - report.exported
23335
+ - report.assessment_stored
23336
+ - report.assessment_link_sent
23337
+ - report.assessment_link_revoked
23338
+ - report.assessment_downloaded
23339
+ - report.assessment_hidden
23340
+ - report.assessment_restored
22280
23341
  - workspace.aggregate_use.changed
23342
+ - workspace.assessment_rollout.changed
23343
+
23344
+ AggregateUseNoticeStatusDto:
23345
+ type: object
23346
+ additionalProperties: false
23347
+ required: [status]
23348
+ description: Current legal-approved opt-in notice, or `unset` while opt-in is unavailable.
23349
+ properties:
23350
+ status:
23351
+ type: string
23352
+ enum: [approved, unset]
23353
+ version:
23354
+ type: string
23355
+ description: Present only when `status` is `approved`. Send it back on opt-in.
23356
+
23357
+ AssessmentNamedEventDto:
23358
+ type: string
23359
+ enum: [major_release, migration, acquisition, policy_change]
23360
+ description: Confounder-relevant named event from the assessment dictionary.
23361
+
23362
+ AssessmentRolloutDto:
23363
+ type: object
23364
+ additionalProperties: false
23365
+ required:
23366
+ - rolloutDay
23367
+ - timezone
23368
+ - boundary
23369
+ - startsAt
23370
+ - setBy
23371
+ - setAt
23372
+ - namedEvents
23373
+ - namedEventsConfirmedBy
23374
+ - namedEventsConfirmedAt
23375
+ properties:
23376
+ rolloutDay:
23377
+ type: string
23378
+ format: date
23379
+ nullable: true
23380
+ description: Confirmed rollout day (`YYYY-MM-DD`); null until set.
23381
+ timezone:
23382
+ type: string
23383
+ enum: [America/New_York]
23384
+ boundary:
23385
+ type: string
23386
+ enum: [midnight]
23387
+ startsAt:
23388
+ type: string
23389
+ format: date-time
23390
+ nullable: true
23391
+ description: UTC instant of 00:00 America/New_York on `rolloutDay`.
23392
+ setBy:
23393
+ type: object
23394
+ nullable: true
23395
+ additionalProperties: false
23396
+ required: [userId, displayName]
23397
+ properties:
23398
+ userId:
23399
+ type: string
23400
+ format: uuid
23401
+ displayName:
23402
+ type: string
23403
+ nullable: true
23404
+ setAt:
23405
+ type: string
23406
+ format: date-time
23407
+ nullable: true
23408
+ namedEvents:
23409
+ type: array
23410
+ nullable: true
23411
+ items:
23412
+ $ref: "#/components/schemas/AssessmentNamedEventDto"
23413
+ description: >
23414
+ Named events confirmed during either matched window. Null means
23415
+ the customer or operator has not confirmed yet, so the ROI
23416
+ confounder check reports `confounder_inputs_missing`. An empty
23417
+ array confirms none occurred.
23418
+ namedEventsConfirmedBy:
23419
+ type: object
23420
+ nullable: true
23421
+ additionalProperties: false
23422
+ required: [userId, displayName]
23423
+ properties:
23424
+ userId:
23425
+ type: string
23426
+ format: uuid
23427
+ displayName:
23428
+ type: string
23429
+ nullable: true
23430
+ namedEventsConfirmedAt:
23431
+ type: string
23432
+ format: date-time
23433
+ nullable: true
23434
+
23435
+ AssessmentRolloutPutDto:
23436
+ type: object
23437
+ additionalProperties: false
23438
+ required: [rolloutDay]
23439
+ properties:
23440
+ rolloutDay:
23441
+ type: string
23442
+ format: date
23443
+ description: Real calendar date, not after today in America/New_York.
23444
+ namedEvents:
23445
+ type: array
23446
+ items:
23447
+ $ref: "#/components/schemas/AssessmentNamedEventDto"
23448
+ description: >
23449
+ Omit to leave the stored named-event confirmation unchanged. Send
23450
+ an empty array to confirm no named event occurred during either
23451
+ matched window, or the events that did.
23452
+
23453
+ AssessmentRoiReasonDto:
23454
+ type: string
23455
+ enum:
23456
+ - rollout_unset
23457
+ - window_missing
23458
+ - missing_cost
23459
+ - non_usd_cost
23460
+ - cost_not_attributable
23461
+ - ship_weight_incomplete
23462
+ - active_engineers_unknown
23463
+ - non_positive_output
23464
+ - normalization_unset
23465
+ - confounder
23466
+ - confounder_inputs_missing
23467
+ - output_unit_mismatch
23468
+ description: >
23469
+ Why the matched-period ROI is not assessable. `cost_not_attributable` means a
23470
+ billing-cycle bill (GitHub Copilot) overlaps the post window and cannot be split
23471
+ by day. `active_engineers_unknown` means stored identities cannot yet be counted.
23472
+ `confounder_inputs_missing` means a named-event, headcount, or repository-scope
23473
+ check has no stored input.
23474
+
23475
+ AssessmentRoiDayRangeDto:
23476
+ type: object
23477
+ additionalProperties: false
23478
+ required: [from, to]
23479
+ properties:
23480
+ from:
23481
+ type: string
23482
+ format: date
23483
+ to:
23484
+ type: string
23485
+ format: date
23486
+
23487
+ AssessmentRoiWindowDto:
23488
+ type: object
23489
+ additionalProperties: false
23490
+ required:
23491
+ - from
23492
+ - to
23493
+ - requiredDays
23494
+ - coveredDays
23495
+ - complete
23496
+ - workingDays
23497
+ - activeEngineers
23498
+ - mergedPullRequests
23499
+ - shipWeight
23500
+ - normalizedShipWeight
23501
+ properties:
23502
+ from:
23503
+ type: string
23504
+ format: date
23505
+ to:
23506
+ type: string
23507
+ format: date
23508
+ requiredDays:
23509
+ type: integer
23510
+ description: Calendar days in the window (90).
23511
+ coveredDays:
23512
+ type: integer
23513
+ description: Calendar days with stored evidence.
23514
+ complete:
23515
+ type: boolean
23516
+ description: Every calendar day is covered and the window ended before today.
23517
+ workingDays:
23518
+ type: integer
23519
+ description: Monday–Friday days in America/New_York. No holidays.
23520
+ activeEngineers:
23521
+ type: integer
23522
+ nullable: true
23523
+ mergedPullRequests:
23524
+ type: integer
23525
+ shipWeight:
23526
+ type: number
23527
+ nullable: true
23528
+ description: Sum of stored Ship Weight for unique merged pull requests; null if any is unscored.
23529
+ normalizedShipWeight:
23530
+ type: number
23531
+ nullable: true
23532
+ description: Ship Weight per active engineer per working day.
22281
23533
 
22282
- UnsetAssessmentDecisionDto:
23534
+ AssessmentRoiDto:
22283
23535
  type: object
22284
23536
  additionalProperties: false
22285
- required: [status]
23537
+ required:
23538
+ - dictionaryVersion
23539
+ - rolloutDay
23540
+ - rolloutBoundary
23541
+ - windows
23542
+ - cost
23543
+ - confounders
23544
+ - status
23545
+ - reason
23546
+ - incrementalNormalizedShipWeight
23547
+ - costPerIncrementalShipWeightUsd
23548
+ - costPerMergedPullRequestUsd
22286
23549
  properties:
22287
- status:
23550
+ dictionaryVersion:
22288
23551
  type: string
22289
- enum: [unset]
22290
-
22291
- AggregateUseNoticeStatusDto:
22292
- type: object
22293
- additionalProperties: false
22294
- required: [status]
22295
- description: Current legal-approved opt-in notice, or `unset` while opt-in is unavailable.
22296
- properties:
22297
- status:
23552
+ rolloutDay:
22298
23553
  type: string
22299
- enum: [approved, unset]
22300
- version:
23554
+ format: date
23555
+ nullable: true
23556
+ rolloutBoundary:
23557
+ type: object
23558
+ additionalProperties: false
23559
+ required: [timezone, boundary, startsAt]
23560
+ properties:
23561
+ timezone:
23562
+ type: string
23563
+ enum: [America/New_York]
23564
+ boundary:
23565
+ type: string
23566
+ enum: [midnight]
23567
+ startsAt:
23568
+ type: string
23569
+ format: date-time
23570
+ nullable: true
23571
+ windows:
23572
+ type: object
23573
+ additionalProperties: false
23574
+ required: [pre, transition, post]
23575
+ properties:
23576
+ pre:
23577
+ allOf:
23578
+ - $ref: "#/components/schemas/AssessmentRoiWindowDto"
23579
+ nullable: true
23580
+ transition:
23581
+ allOf:
23582
+ - $ref: "#/components/schemas/AssessmentRoiDayRangeDto"
23583
+ nullable: true
23584
+ post:
23585
+ allOf:
23586
+ - $ref: "#/components/schemas/AssessmentRoiWindowDto"
23587
+ nullable: true
23588
+ cost:
23589
+ type: object
23590
+ additionalProperties: false
23591
+ required: [amountUsd, currency, providers]
23592
+ description: Recorded daily AI spend inside the post window. Never estimated.
23593
+ properties:
23594
+ amountUsd:
23595
+ type: number
23596
+ nullable: true
23597
+ currency:
23598
+ type: string
23599
+ enum: [USD]
23600
+ nullable: true
23601
+ providers:
23602
+ type: array
23603
+ items:
23604
+ type: string
23605
+ confounders:
23606
+ type: object
23607
+ additionalProperties: false
23608
+ required: [namedEvent, headcountChangeRatio, repositoryScopeChangeRatio]
23609
+ properties:
23610
+ namedEvent:
23611
+ type: boolean
23612
+ nullable: true
23613
+ description: Null when no named-event input is stored.
23614
+ headcountChangeRatio:
23615
+ type: number
23616
+ nullable: true
23617
+ repositoryScopeChangeRatio:
23618
+ type: number
23619
+ nullable: true
23620
+ description: Repositories with merged pull requests in only one window, over pre-window repositories.
23621
+ status:
22301
23622
  type: string
22302
- description: Present only when `status` is `approved`. Send it back on opt-in.
23623
+ enum: [assessable, not_assessable]
23624
+ reason:
23625
+ allOf:
23626
+ - $ref: "#/components/schemas/AssessmentRoiReasonDto"
23627
+ nullable: true
23628
+ incrementalNormalizedShipWeight:
23629
+ type: number
23630
+ nullable: true
23631
+ costPerIncrementalShipWeightUsd:
23632
+ type: number
23633
+ nullable: true
23634
+ costPerMergedPullRequestUsd:
23635
+ type: number
23636
+ nullable: true
23637
+ description: Separate correlation. Never the sold ROI figure.
22303
23638
 
22304
23639
  AggregateUseConsentDto:
22305
23640
  type: object
@@ -22404,7 +23739,7 @@ components:
22404
23739
  properties:
22405
23740
  version:
22406
23741
  type: string
22407
- enum: ["2026-10-20.1"]
23742
+ enum: ["2026-10-24.1"]
22408
23743
  periodDays:
22409
23744
  type: integer
22410
23745
  enum: [90]
@@ -22576,9 +23911,21 @@ components:
22576
23911
  - countryAllowList
22577
23912
  properties:
22578
23913
  baselinePrice:
22579
- $ref: "#/components/schemas/UnsetAssessmentDecisionDto"
23914
+ type: object
23915
+ additionalProperties: false
23916
+ required: [status, amountUsd, taxes]
23917
+ properties:
23918
+ status: { type: string, enum: [set] }
23919
+ amountUsd: { type: integer, enum: [4999] }
23920
+ taxes: { type: string, enum: [extra] }
22580
23921
  premiumPrice:
22581
- $ref: "#/components/schemas/UnsetAssessmentDecisionDto"
23922
+ type: object
23923
+ additionalProperties: false
23924
+ required: [status, amountUsd, taxes]
23925
+ properties:
23926
+ status: { type: string, enum: [set] }
23927
+ amountUsd: { type: integer, enum: [9999] }
23928
+ taxes: { type: string, enum: [extra] }
22582
23929
  currency:
22583
23930
  type: object
22584
23931
  additionalProperties: false
@@ -22641,7 +23988,18 @@ components:
22641
23988
  enum: [monday, tuesday, wednesday, thursday, friday]
22642
23989
  holidays: { type: string, enum: [none] }
22643
23990
  countryAllowList:
22644
- $ref: "#/components/schemas/UnsetAssessmentDecisionDto"
23991
+ type: object
23992
+ additionalProperties: false
23993
+ required: [status, countries, counselAccepted]
23994
+ description: Owner decision without counsel acceptance; `counselAccepted` stays false.
23995
+ properties:
23996
+ status: { type: string, enum: [set] }
23997
+ countries:
23998
+ type: array
23999
+ items:
24000
+ type: string
24001
+ enum: [US, BH, KW, OM, QA, SA, AE]
24002
+ counselAccepted: { type: boolean, enum: [false] }
22645
24003
 
22646
24004
  ReportCenterExportRecordBodyDto:
22647
24005
  type: object
@@ -22727,6 +24085,286 @@ components:
22727
24085
  minLength: 1
22728
24086
  maxLength: 256
22729
24087
 
24088
+ StoreAssessmentReportBodyDto:
24089
+ type: object
24090
+ additionalProperties: false
24091
+ required: [pdfBase64, build, contentHash]
24092
+ properties:
24093
+ pdfBase64:
24094
+ type: string
24095
+ description: The buyer PDF, base64-encoded. Must start with `%PDF-`; at most 10 MiB decoded.
24096
+ build:
24097
+ $ref: "#/components/schemas/BuildAssessmentManifestBodyDto"
24098
+ contentHash:
24099
+ type: string
24100
+ pattern: "^[0-9a-f]{64}$"
24101
+ description: >
24102
+ `result.contentHash` from the `buildWorkspaceAssessmentManifest` call the PDF was
24103
+ rendered from. The server rebuilds the manifest and rejects a different hash.
24104
+
24105
+ BuildAssessmentManifestBodyDto:
24106
+ type: object
24107
+ additionalProperties: false
24108
+ required: [offerId, shareMode, sections]
24109
+ properties:
24110
+ offerId:
24111
+ type: string
24112
+ enum: [engineering_readiness_baseline, agentic_ai_roi]
24113
+ shareMode:
24114
+ type: string
24115
+ enum: [internal, external]
24116
+ sections:
24117
+ type: array
24118
+ minItems: 1
24119
+ maxItems: 64
24120
+ description: Every selected section with its render status. Ids are unique.
24121
+ items:
24122
+ type: object
24123
+ additionalProperties: false
24124
+ required: [id, status]
24125
+ properties:
24126
+ id:
24127
+ type: string
24128
+ minLength: 1
24129
+ maxLength: 128
24130
+ status:
24131
+ type: string
24132
+ enum: [ok, failed, omitted]
24133
+ reason:
24134
+ type: string
24135
+ maxLength: 500
24136
+ roi:
24137
+ description: >
24138
+ The matched-period ROI chapter as printed, copied from the
24139
+ `getWorkspaceAssessmentRoi` result it rendered. Null or omitted when the chapter
24140
+ did not print one.
24141
+ allOf:
24142
+ - $ref: "#/components/schemas/PrintedAssessmentRoiDto"
24143
+ nullable: true
24144
+
24145
+ PrintedAssessmentRoiDto:
24146
+ type: object
24147
+ additionalProperties: false
24148
+ required:
24149
+ - status
24150
+ - reason
24151
+ - incrementalNormalizedShipWeight
24152
+ - costPerIncrementalShipWeightUsd
24153
+ - costPerMergedPullRequestUsd
24154
+ properties:
24155
+ status:
24156
+ type: string
24157
+ enum: [assessable, not_assessable]
24158
+ reason:
24159
+ type: string
24160
+ nullable: true
24161
+ incrementalNormalizedShipWeight:
24162
+ type: number
24163
+ nullable: true
24164
+ costPerIncrementalShipWeightUsd:
24165
+ type: number
24166
+ nullable: true
24167
+ costPerMergedPullRequestUsd:
24168
+ type: number
24169
+ nullable: true
24170
+
24171
+ BuildAssessmentManifestResponseDto:
24172
+ type: object
24173
+ required: [validation, result]
24174
+ properties:
24175
+ validation:
24176
+ $ref: "#/components/schemas/AssessmentManifestValidationBodyDto"
24177
+ result:
24178
+ $ref: "#/components/schemas/AssessmentManifestValidationResultDto"
24179
+
24180
+ StoreAssessmentReportRejectedDto:
24181
+ type: object
24182
+ required: [error, code]
24183
+ properties:
24184
+ error:
24185
+ type: string
24186
+ code:
24187
+ type: string
24188
+ enum: [not_buyer_deliverable, manifest_changed]
24189
+ validation:
24190
+ type: string
24191
+ enum: [validated, not_validated]
24192
+ reasons:
24193
+ type: array
24194
+ items:
24195
+ type: object
24196
+ required: [code, message]
24197
+ properties:
24198
+ code:
24199
+ type: string
24200
+ message:
24201
+ type: string
24202
+
24203
+ AssessmentReportLinkDto:
24204
+ type: object
24205
+ required:
24206
+ [
24207
+ id,
24208
+ reportId,
24209
+ recipientUserId,
24210
+ recipientEmail,
24211
+ recipientName,
24212
+ expiresAt,
24213
+ revokedAt,
24214
+ createdAt,
24215
+ lastAccessedAt,
24216
+ status,
24217
+ ]
24218
+ properties:
24219
+ id:
24220
+ type: string
24221
+ format: uuid
24222
+ reportId:
24223
+ type: string
24224
+ format: uuid
24225
+ recipientUserId:
24226
+ type: string
24227
+ format: uuid
24228
+ recipientEmail:
24229
+ type: string
24230
+ nullable: true
24231
+ description: Null when the recipient is no longer a workspace member.
24232
+ recipientName:
24233
+ type: string
24234
+ nullable: true
24235
+ expiresAt:
24236
+ type: string
24237
+ format: date-time
24238
+ revokedAt:
24239
+ type: string
24240
+ format: date-time
24241
+ nullable: true
24242
+ createdAt:
24243
+ type: string
24244
+ format: date-time
24245
+ lastAccessedAt:
24246
+ type: string
24247
+ format: date-time
24248
+ nullable: true
24249
+ status:
24250
+ type: string
24251
+ enum: [active, expired, revoked]
24252
+
24253
+ AssessmentReportDto:
24254
+ type: object
24255
+ required:
24256
+ [
24257
+ id,
24258
+ offerId,
24259
+ sizeBytes,
24260
+ sha256,
24261
+ manifestHash,
24262
+ createdAt,
24263
+ createdBy,
24264
+ hiddenAt,
24265
+ restoreUntil,
24266
+ status,
24267
+ links,
24268
+ ]
24269
+ properties:
24270
+ id:
24271
+ type: string
24272
+ format: uuid
24273
+ offerId:
24274
+ type: string
24275
+ enum: [engineering_readiness_baseline, agentic_ai_roi]
24276
+ sizeBytes:
24277
+ type: integer
24278
+ sha256:
24279
+ type: string
24280
+ description: sha256 of the plaintext PDF.
24281
+ manifestHash:
24282
+ type: string
24283
+ description: "`contentHash` of the validated manifest."
24284
+ createdAt:
24285
+ type: string
24286
+ format: date-time
24287
+ createdBy:
24288
+ type: string
24289
+ format: uuid
24290
+ nullable: true
24291
+ hiddenAt:
24292
+ type: string
24293
+ format: date-time
24294
+ nullable: true
24295
+ restoreUntil:
24296
+ type: string
24297
+ format: date-time
24298
+ nullable: true
24299
+ description: End of the restore window. Not an erasure or backup purge date.
24300
+ status:
24301
+ type: string
24302
+ enum: [available, hidden_restorable, restore_window_ended]
24303
+ links:
24304
+ type: array
24305
+ items:
24306
+ $ref: "#/components/schemas/AssessmentReportLinkDto"
24307
+
24308
+ AssessmentReportListDto:
24309
+ type: object
24310
+ required: [restoreWindowDays, secureLinkDays, reports]
24311
+ properties:
24312
+ restoreWindowDays:
24313
+ type: integer
24314
+ enum: [30]
24315
+ secureLinkDays:
24316
+ type: integer
24317
+ enum: [14]
24318
+ description: Default and maximum secure link lifetime in days.
24319
+ reports:
24320
+ type: array
24321
+ items:
24322
+ $ref: "#/components/schemas/AssessmentReportDto"
24323
+
24324
+ CreateAssessmentReportLinkBodyDto:
24325
+ type: object
24326
+ additionalProperties: false
24327
+ required: [recipientUserId]
24328
+ properties:
24329
+ recipientUserId:
24330
+ type: string
24331
+ format: uuid
24332
+ description: A current workspace member (`TeamMemberDto.id`).
24333
+ expiresInDays:
24334
+ type: integer
24335
+ minimum: 1
24336
+ maximum: 14
24337
+ default: 14
24338
+
24339
+ CreateAssessmentReportLinkResponseDto:
24340
+ type: object
24341
+ required: [link, email]
24342
+ properties:
24343
+ link:
24344
+ $ref: "#/components/schemas/AssessmentReportLinkDto"
24345
+ email:
24346
+ type: string
24347
+ enum: [sent, skipped_missing_smtp, skipped_missing_base_url, failed]
24348
+
24349
+ AssessmentReportAccessErrorDto:
24350
+ type: object
24351
+ required: [error, code]
24352
+ properties:
24353
+ error:
24354
+ type: string
24355
+ code:
24356
+ type: string
24357
+ enum:
24358
+ [
24359
+ leaked_token,
24360
+ removed_member,
24361
+ wrong_workspace,
24362
+ expired,
24363
+ revoked,
24364
+ invalid_grant,
24365
+ report_hidden,
24366
+ ]
24367
+
22730
24368
  ReportCenterExportRecordResponseDto:
22731
24369
  type: object
22732
24370
  required: [ok, activityEntryId]