openapi: 3.1.0 info: title: CDP Public API — Stewardship Admin Actions version: 1.0.0 description: > Write endpoints for OSSPREY Program admin stewardship actions. **Authentication:** OAuth 2.0 M2M bearer token. All endpoints require `write:stewardships`. **V1 note:** Scope `write:stewardships` is not yet added to the Auth0 staging tenant — scope enforcement is temporarily disabled. See CM-1235. servers: - url: https://cm.lfx.dev/api/v1/akrites description: Production - url: https://lf-staging.crowd.dev/api/v1/akrites description: Staging tags: - name: Stewardship Actions description: Admin-initiated stewardship mutations. components: securitySchemes: M2MBearer: type: http scheme: bearer bearerFormat: JWT schemas: Error: type: object required: [error] properties: error: type: object required: [code, message] properties: code: type: string example: NOT_FOUND message: type: string example: Stewardship not found. ActivityActor: type: object required: [userId] description: Profile of the actor who performed an activity. Stored as a snapshot on the activity log. properties: userId: type: string description: Auth0 sub of the actor. example: auth0|abc123 username: type: - string - 'null' description: LFX username of the actor. example: gaspergrom displayName: type: - string - 'null' description: Full display name of the actor. example: Gašper Grom avatarUrl: type: - string - 'null' format: uri description: Avatar URL of the actor. example: 'https://avatars.githubusercontent.com/u/12345' ActorInput: type: object required: [userId] description: > Profile of the actor performing this action. Stored as a snapshot on the activity log. `userId` is required. All other fields are optional and can be null. properties: userId: type: string minLength: 1 description: Auth0 sub of the actor. Must match the authenticated user's token sub. example: auth0|abc123 username: type: - string - 'null' minLength: 1 description: LFX username of the actor. example: gaspergrom displayName: type: - string - 'null' minLength: 1 description: Full display name of the actor. example: Gašper Grom avatarUrl: type: - string - 'null' format: uri description: Avatar URL of the actor. example: 'https://avatars.githubusercontent.com/u/12345' StewardshipStatus: type: string enum: - unassigned - open - assessing - active - needs_attention - escalated - blocked - inactive StewardshipRecord: type: object required: [id, packageId, status, origin, version, createdAt, updatedAt] properties: id: type: string example: '42' packageId: type: string example: '1234' status: $ref: '#/components/schemas/StewardshipStatus' origin: type: string enum: [auto_imported, self_claimed, assigned, opened_for_claim] version: type: integer example: 1 openedAt: type: - string - 'null' format: date-time lastStatusAt: type: - string - 'null' format: date-time inactiveReason: type: - string - 'null' enum: - quarterly_cadence_missed - stepped_down - no_longer_critical - 'null' resolutionPath: description: Set on `escalated` status. Null for all other statuses. oneOf: - $ref: '#/components/schemas/EscalationResolutionPath' - type: 'null' statusNote: type: - string - 'null' description: Free-text note for the current status. Set by escalate or updateStatus. Null on open. example: Contacted maintainer, no response after 30 days. createdAt: type: string format: date-time updatedAt: type: string format: date-time StewardRecord: type: object required: [id, stewardshipId, userId, role, assignedAt] properties: id: type: string example: '7' stewardshipId: type: string example: '42' userId: type: string description: Auth0 sub of the assigned steward. example: abc123 username: type: - string - 'null' description: LFX username of the steward. Null if not yet stored. example: joanagmaia displayName: type: - string - 'null' description: Full display name of the steward. Null if not yet stored. example: Joana Maia role: type: string enum: [lead, co_steward] assignedAt: type: string format: date-time assignedBy: type: - string - 'null' description: Auth0 sub of the admin who assigned this steward. example: xyz789 EscalationResolutionPath: type: string enum: - right_of_first_refusal - replace_the_dependency - find_vendor_for_lts - consortium_adopts_maintainership - compensating_controls_monitor - namespace_takeover paths: /stewardships/open: post: operationId: openStewardship summary: Open a package for stewardship description: > Transitions the stewardship status to `open`, marking the package as available for claiming. If a stewardship row does not exist yet, one is created. If the stewardship is already `open`, this is a no-op (idempotent). tags: - Stewardship Actions security: - M2MBearer: - write:stewardships requestBody: required: true content: application/json: schema: type: object required: [purl, actor] properties: purl: type: string description: Package URL (must start with `pkg:`). example: pkg:npm/lodash actor: $ref: '#/components/schemas/ActorInput' example: purl: pkg:npm/lodash actor: userId: auth0|abc123 username: gaspergrom displayName: Gašper Grom avatarUrl: 'https://avatars.githubusercontent.com/u/12345' responses: '200': description: Stewardship opened (or already open). content: application/json: schema: type: object required: [stewardship] properties: stewardship: $ref: '#/components/schemas/StewardshipRecord' example: stewardship: id: '42' packageId: '1234' status: open origin: opened_for_claim version: 1 openedAt: '2026-06-15T10:00:00Z' lastStatusAt: '2026-06-15T10:00:00Z' inactiveReason: null createdAt: '2026-06-15T10:00:00Z' updatedAt: '2026-06-15T10:00:00Z' '400': description: Validation error (e.g. missing or invalid purl). content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: Missing or invalid bearer token. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Insufficient scopes. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: Package not found. content: application/json: schema: $ref: '#/components/schemas/Error' /stewardships/{id}/assign: post: operationId: assignSteward summary: Assign a steward to a stewardship description: > Assigns a user as a steward with the given role. If the user is already an active steward, the role is updated (soft-delete + re-insert). Returns the unchanged stewardship record and the full active stewards list after the operation. tags: - Stewardship Actions security: - M2MBearer: - write:stewardships parameters: - name: id in: path required: true schema: type: integer description: Stewardship ID. example: 42 requestBody: required: true content: application/json: schema: type: object required: [steward, actor] properties: steward: type: object required: [userId, role] description: > The user to assign as steward. `username` and `displayName` must be provided together or both omitted — sending one without the other returns 400. properties: userId: type: string minLength: 1 description: Auth0 sub of the user to assign as steward. example: abc123 username: type: - string - 'null' description: LFX username of the steward. Must be provided together with `displayName`. example: joanagmaia displayName: type: - string - 'null' description: Full display name of the steward. Must be provided together with `username`. example: Joana Maia role: type: string enum: [lead, co_steward] note: type: string minLength: 1 description: Optional free-text note for the activity log. moveToAssessing: type: boolean default: false description: > If true, atomically transitions the stewardship status to `assessing` in the same transaction as the assignment. Use for the "Assign & move to Assessing" action to avoid a second round-trip. actor: $ref: '#/components/schemas/ActorInput' example: steward: userId: abc123 role: lead moveToAssessing: true actor: userId: auth0|xyz username: gaspergrom displayName: Gašper Grom avatarUrl: 'https://avatars.githubusercontent.com/u/12345' responses: '200': description: Steward assigned. content: application/json: schema: type: object required: [stewardship, stewards] properties: stewardship: $ref: '#/components/schemas/StewardshipRecord' stewards: type: array items: $ref: '#/components/schemas/StewardRecord' example: stewardship: id: '42' packageId: '1234' status: open origin: opened_for_claim version: 1 openedAt: '2026-06-15T10:00:00Z' lastStatusAt: '2026-06-15T10:00:00Z' inactiveReason: null createdAt: '2026-06-15T10:00:00Z' updatedAt: '2026-06-15T10:00:00Z' stewards: - id: '7' stewardshipId: '42' userId: abc123 role: lead assignedAt: '2026-06-15T10:05:00Z' assignedBy: xyz789 '400': description: Validation error (e.g. invalid role). content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: Missing or invalid bearer token. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Insufficient scopes. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: Stewardship not found. content: application/json: schema: $ref: '#/components/schemas/Error' /stewardships/{id}/escalate: post: operationId: escalateStewardship summary: Escalate a stewardship description: > Transitions the stewardship to `escalated` status and logs the chosen resolution path in the activity log. Optional free-text notes can be included. tags: - Stewardship Actions security: - M2MBearer: - write:stewardships parameters: - name: id in: path required: true schema: type: integer description: Stewardship ID. example: 42 requestBody: required: true content: application/json: schema: type: object required: [resolutionPath, actor] properties: resolutionPath: $ref: '#/components/schemas/EscalationResolutionPath' notes: type: string minLength: 1 description: Optional free-text notes for the activity log. example: Contacted maintainer, no response after 30 days. actor: $ref: '#/components/schemas/ActorInput' example: resolutionPath: right_of_first_refusal notes: Contacted maintainer, no response after 30 days. actor: userId: auth0|abc123 username: gaspergrom displayName: Gašper Grom avatarUrl: 'https://avatars.githubusercontent.com/u/12345' responses: '200': description: Stewardship escalated. content: application/json: schema: type: object required: [stewardship] properties: stewardship: $ref: '#/components/schemas/StewardshipRecord' example: stewardship: id: '42' packageId: '1234' status: escalated origin: opened_for_claim version: 1 openedAt: '2026-06-15T10:00:00Z' lastStatusAt: '2026-06-15T11:00:00Z' inactiveReason: null createdAt: '2026-06-15T10:00:00Z' updatedAt: '2026-06-15T11:00:00Z' '400': description: Validation error (e.g. invalid resolutionPath). content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: Missing or invalid bearer token. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Insufficient scopes. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: Stewardship not found. content: application/json: schema: $ref: '#/components/schemas/Error' /stewardships/{id}/status: patch: operationId: updateStewardshipStatus summary: Update stewardship status description: > Updates the stewardship status and logs a `state_changed` activity entry. Valid target statuses: `assessing`, `active`, `needs_attention`, `blocked`, `inactive`. When transitioning to `inactive`, `inactiveReason` is required. tags: - Stewardship Actions security: - M2MBearer: - write:stewardships parameters: - name: id in: path required: true schema: type: integer description: Stewardship ID. example: 42 requestBody: required: true content: application/json: schema: type: object required: [status, actor] properties: status: type: string enum: - assessing - active - needs_attention - blocked - inactive inactiveReason: type: string enum: - quarterly_cadence_missed - stepped_down - no_longer_critical description: Required when `status` is `inactive`. notes: type: string minLength: 1 description: Optional free-text notes for the activity log. actor: $ref: '#/components/schemas/ActorInput' examples: set_active: summary: Transition to active value: status: active actor: userId: auth0|abc123 username: gaspergrom displayName: Gašper Grom avatarUrl: 'https://avatars.githubusercontent.com/u/12345' set_inactive: summary: Transition to inactive (inactiveReason required) value: status: inactive inactiveReason: stepped_down notes: Steward stepped down voluntarily. actor: userId: auth0|abc123 username: gaspergrom displayName: Gašper Grom avatarUrl: 'https://avatars.githubusercontent.com/u/12345' set_blocked: summary: Transition to blocked value: status: blocked notes: Waiting on upstream maintainer response. actor: userId: auth0|abc123 username: gaspergrom displayName: Gašper Grom avatarUrl: 'https://avatars.githubusercontent.com/u/12345' responses: '200': description: Status updated. content: application/json: schema: type: object required: [stewardship] properties: stewardship: $ref: '#/components/schemas/StewardshipRecord' example: stewardship: id: '42' packageId: '1234' status: active origin: opened_for_claim version: 1 openedAt: '2026-06-15T10:00:00Z' lastStatusAt: '2026-06-15T12:00:00Z' inactiveReason: null createdAt: '2026-06-15T10:00:00Z' updatedAt: '2026-06-15T12:00:00Z' '400': description: > Validation error. Includes the case where `status` is `inactive` but `inactiveReason` is missing. content: application/json: schema: $ref: '#/components/schemas/Error' '401': description: Missing or invalid bearer token. content: application/json: schema: $ref: '#/components/schemas/Error' '403': description: Insufficient scopes. content: application/json: schema: $ref: '#/components/schemas/Error' '404': description: Stewardship not found. content: application/json: schema: $ref: '#/components/schemas/Error'