{"openapi":"3.1.0","info":{"title":"ProjeX Submission API","version":"1.0.0","description":"Public REST API for discovering live cohorts, submitting milestones/tasks,\nand administering cohorts, teams and membership.\n\nAuthenticate with an API key from **Account → API keys**:\n\n- `Authorization: Bearer pjx_live_…`\n- or `x-api-key: pjx_live_…`\n\nScopes: `cohorts:read`, `cohorts:write`, `milestones:read`, `submissions:read`,\n`submissions:write`, `teams:read`, `teams:write`, `cohort-members:write`,\n`team-members:write`.\n\nA scope only *narrows* what the key can do — every admin call is also checked\nagainst the role of the user the key was issued to, in that specific cohort or\nteam. An external caller can never exceed its owner.\n\nMembers are identified by **email** on every admin route; internal membership\nids are never exposed.\n\nTypeScript SDK: `@pkg-projex/sdk` — see [/docs/sdk](/docs/sdk)."},"servers":[{"url":"https://projex.xceleratordemo.in/api/v1","description":"Current environment"}],"tags":[{"name":"Cohorts"},{"name":"Teams"},{"name":"Members"},{"name":"Milestones"},{"name":"Tasks"},{"name":"Uploads"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key (`pjx_live_…`)"},"apiKeyHeader":{"type":"apiKey","in":"header","name":"x-api-key"}},"responses":{"Unauthorized":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"Forbidden":{"description":"Insufficient scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"Conflict":{"description":"State conflict. `details.reason` is machine-readable: `last_manager`, `team_has_learners`, `solo_team_locked`, `user_banned`, `role_locked_to_cohort`, `not_cohort_member`, `individual_cohort`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"ValidationError":{"description":"Invalid request body or parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"schemas":{"ErrorEnvelope":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"requestId":{"type":"string"},"details":{}},"required":["code","message"]}},"required":["error"]},"Cohort":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":["string","null"]},"visibility":{"type":["string","null"]},"startDate":{"type":["string","null"],"format":"date-time"},"endDate":{"type":["string","null"],"format":"date-time"},"status":{"type":"string","enum":["planned","live","closed"]},"skills":{"type":"array","items":{"type":"string"},"description":"Present when skills were set on create/update."}}},"CohortInput":{"type":"object","required":["projectId","name","startDate","endDate"],"properties":{"projectId":{"type":"string","description":"Project template the cohort runs from."},"name":{"type":"string"},"type":{"type":"string","enum":["team","individual"],"default":"team","description":"`individual` cohorts auto-create a solo team per learner."},"visibility":{"type":"string","enum":["public","private"],"default":"private"},"clusterId":{"type":["string","null"],"description":"Required when visibility is private. Its roster seeds the cohort."},"startDate":{"type":"string","format":"date-time"},"endDate":{"type":"string","format":"date-time"},"skills":{"type":"array","items":{"type":"string"},"description":"Skill labels to tag on the cohort (resolved to verified skills or pending submissions)."}}},"CohortUpdate":{"type":"object","description":"All fields optional; only what you send changes.","properties":{"name":{"type":"string"},"visibility":{"type":"string","enum":["public","private"]},"clusterId":{"type":["string","null"]},"startDate":{"type":"string","format":"date-time"},"endDate":{"type":"string","format":"date-time"},"skills":{"type":"array","items":{"type":"string"},"description":"When set, replaces cohort skill tags (empty clears)."}}},"Team":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"integer"},"name":{"type":"string"},"description":{"type":["string","null"]},"cohortId":{"type":"string"},"teamKind":{"type":"string","enum":["multi","solo"]},"memberCount":{"type":"integer"},"staffCount":{"type":"integer"}}},"TeamInput":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"coverImage":{"type":["string","null"]}}},"TeamUpdate":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"overview":{"type":"string"},"coverImage":{"type":["string","null"]}}},"CohortMemberInput":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email","description":"The app user, org membership and cohort enrolment are created if missing."},"role":{"type":"string","enum":["manager","mentor","evaluator","learner"],"default":"learner"},"name":{"type":"string","description":"Used only if the user must be created."},"teamId":{"type":"string","description":"Optional placement. Without it a learner in a team-cohort is enrolled but team-less and cannot submit yet."},"notify":{"type":"boolean","description":"Send the onboarding email — newly created users only."}}},"TeamMemberInput":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"},"role":{"type":"string","enum":["learner","mentor","evaluator"],"default":"learner","description":"Selects the roster: learner → team members, mentor/evaluator → team staff."},"name":{"type":"string"},"notify":{"type":"boolean"}}},"MemberRoleChange":{"type":"object","required":["email","role"],"properties":{"email":{"type":"string","format":"email"},"role":{"type":"string"}}},"MemberEmail":{"type":"object","required":["email"],"properties":{"email":{"type":"string","format":"email"}}},"ProvisioningSummary":{"type":"object","description":"What existed already vs. what this call created. Re-adding an existing member is an idempotent no-op, not an error.","properties":{"userId":{"type":"string"},"cohortMemberId":{"type":"string"},"role":{"type":"string"},"team":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"created":{"type":"object","properties":{"user":{"type":"boolean"},"orgMember":{"type":"boolean"},"cohortMember":{"type":"boolean"},"teamMember":{"type":"boolean"}}}}},"Milestone":{"type":"object","properties":{"id":{"type":"string"},"ref":{"type":["string","null"]},"publicId":{"type":["integer","null"]},"title":{"type":"string"},"description":{"type":["string","null"]},"order":{"type":["integer","null"]},"startDate":{"type":["string","null"],"format":"date-time"},"endDate":{"type":["string","null"],"format":"date-time"},"submissionType":{"type":["string","null"]}}},"Task":{"type":"object","properties":{"id":{"type":"string"},"ref":{"type":"string","example":"TS42"},"publicId":{"type":"integer"},"title":{"type":"string"},"description":{"type":["string","null"]},"status":{"type":"string"},"priority":{"type":"string"},"milestoneId":{"type":["string","null"]},"deadline":{"type":["string","null"],"format":"date-time"}}},"MilestoneSubmissionInput":{"type":"object","required":["title","submission"],"properties":{"teamId":{"type":"string","description":"Optional when the user is on exactly one team; required if ambiguous."},"title":{"type":"string"},"submission":{"type":"string"},"contributionAreas":{"type":"array","items":{}},"reflectionAnswers":{"type":"array","items":{"type":"object","properties":{"questionId":{"type":"string"},"answer":{"type":"string"}}}},"replacesSubmissionId":{"type":"string"},"attachments":{"type":"array","description":"Files uploaded via POST /uploads. Each references the returned fileKey (must belong to the caller).","items":{"$ref":"#/components/schemas/Attachment"}}}},"TaskSubmissionInput":{"type":"object","required":["title","submission"],"properties":{"title":{"type":"string"},"submission":{"type":"string"},"replacesSubmissionId":{"type":"string"},"attachments":{"type":"array","description":"Files uploaded via POST /uploads. Each references the returned fileKey (must belong to the caller).","items":{"$ref":"#/components/schemas/Attachment"}}}},"Attachment":{"type":"object","required":["fileKey"],"properties":{"fileKey":{"type":"string","description":"The fileKey returned by POST /uploads (namespaced to the caller)."},"fileName":{"type":"string"}}},"UploadRequest":{"type":"object","required":["fileName","contentType"],"properties":{"fileName":{"type":"string"},"contentType":{"type":"string"},"size":{"type":"integer"}}},"UploadResult":{"type":"object","properties":{"uploadUrl":{"type":"string"},"fileKey":{"type":"string"},"publicUrl":{"type":"string","description":"Public URL of the file once uploaded — usable directly as a submission value."},"expiresIn":{"type":"integer"}}}}},"security":[{"bearerAuth":[]},{"apiKeyHeader":[]}],"paths":{"/cohorts":{"get":{"tags":["Cohorts"],"summary":"List enrolled cohorts","operationId":"listCohorts","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["planned","live","closed"]}},{"name":"q","in":"query","description":"Free-text search (matches name/title).","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size. Defaults to 50, clamped to a max of 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Opaque cursor from a previous page’s `nextCursor`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Cohorts the caller belongs to","content":{"application/json":{"schema":{"type":"object","properties":{"cohorts":{"type":"array","items":{"$ref":"#/components/schemas/Cohort"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page; null when there are no more results."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}},"post":{"tags":["Cohorts"],"summary":"Create a cohort","description":"Requires `cohorts:write` **and** manage-project permission on the template. Enrols the acting user as the cohort manager, seeds the cluster roster, and auto-creates solo teams for `individual` cohorts.","operationId":"createCohort","parameters":[{"name":"dryRun","in":"query","description":"Validate and authorize without writing. Returns a preview/impact body.","schema":{"type":"boolean"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CohortInput"}}}},"responses":{"200":{"description":"The created cohort","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cohort"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/cohorts/{cohortId}":{"get":{"tags":["Cohorts"],"summary":"Get cohort detail + teams","operationId":"getCohort","parameters":[{"name":"cohortId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Cohort with the caller’s teams","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Cohort"},{"type":"object","properties":{"teams":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"publicId":{"type":"integer"},"name":{"type":"string"}}}}}}]}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Cohorts"],"summary":"Update a cohort","description":"Requires `cohorts:write` + edit permission on the cohort. Making a cohort private while join requests are pending is rejected.","operationId":"updateCohort","parameters":[{"name":"cohortId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dryRun","in":"query","description":"Validate and authorize without writing. Returns a preview/impact body.","schema":{"type":"boolean"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CohortUpdate"}}}},"responses":{"200":{"description":"The updated cohort","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cohort"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationError"}}},"delete":{"tags":["Cohorts"],"summary":"Delete a cohort","description":"Soft-deletes the cohort and cascades to its teams, members, milestones and submissions. Nothing is hard-deleted. Call with `?dryRun=true` first to see the cascade.","operationId":"deleteCohort","parameters":[{"name":"cohortId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dryRun","in":"query","description":"Validate and authorize without writing. Returns a preview/impact body.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"What was deleted — or, for a dry run, what would be.","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"deleted":{"type":"object","properties":{"teams":{"type":"integer"},"members":{"type":"integer"},"milestones":{"type":"integer"}}},"impact":{"type":"object","description":"Returned instead of `deleted` when dryRun=true."}}}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/cohorts/{cohortId}/teams":{"get":{"tags":["Teams"],"summary":"List teams in a cohort","operationId":"listCohortTeams","parameters":[{"name":"cohortId","in":"path","required":true,"schema":{"type":"string"}},{"name":"teamKind","in":"query","description":"'solo' teams are auto-created per learner in an `individual` cohort; 'multi' teams are the ones you create. Omit for both.","schema":{"type":"string","enum":["multi","solo"]}},{"name":"memberEmail","in":"query","description":"Only teams this person sits on, as learner or staff. 404s if no user has that email.","schema":{"type":"string","format":"email"}},{"name":"q","in":"query","description":"Free-text search (matches name/title).","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size. Defaults to 50, clamped to a max of 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Opaque cursor from a previous page’s `nextCursor`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Teams","content":{"application/json":{"schema":{"type":"object","properties":{"teams":{"type":"array","items":{"$ref":"#/components/schemas/Team"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page; null when there are no more results."}}}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}},"post":{"tags":["Teams"],"summary":"Create a team","description":"Multi-member teams only — `individual` cohorts create a solo team per learner automatically and reject this call with `409 individual_cohort`.","operationId":"createTeam","parameters":[{"name":"cohortId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dryRun","in":"query","description":"Validate and authorize without writing. Returns a preview/impact body.","schema":{"type":"boolean"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamInput"}}}},"responses":{"200":{"description":"The created team","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Team"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}}},"/cohorts/{cohortId}/members":{"post":{"tags":["Members"],"summary":"Add a cohort member by email","description":"Walks the whole chain — app user → org member → cohort enrolment → optional team placement — and no-ops on each layer that already exists, so retries are safe. Members are enrolled immediately (`accepted: true`); there is no join-request step.","operationId":"addCohortMember","parameters":[{"name":"cohortId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dryRun","in":"query","description":"Validate and authorize without writing. Returns a preview/impact body.","schema":{"type":"boolean"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CohortMemberInput"}}}},"responses":{"200":{"description":"Provisioning summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningSummary"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationError"}}},"patch":{"tags":["Members"],"summary":"Change a member's cohort role","description":"Moves membership rows only — submissions, evaluations, ratings and comments are untouched. It does reshuffle team placement, reported as `rosterChanges`.","operationId":"changeCohortMemberRole","parameters":[{"name":"cohortId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dryRun","in":"query","description":"Validate and authorize without writing. Returns a preview/impact body.","schema":{"type":"boolean"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberRoleChange"}}}},"responses":{"200":{"description":"New role + roster diff","content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string"},"previousRole":{"type":"string"},"role":{"type":"string"},"rosterChanges":{"type":"object"}}}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}},"delete":{"tags":["Members"],"summary":"Remove a member from the cohort","description":"Shallow: soft-deletes the cohort enrolment (and their teams in this cohort). The app user and their org membership survive — they may belong to other cohorts.","operationId":"removeCohortMember","parameters":[{"name":"cohortId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dryRun","in":"query","description":"Validate and authorize without writing. Returns a preview/impact body.","schema":{"type":"boolean"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberEmail"}}}},"responses":{"200":{"description":"Removed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"userId":{"type":"string"},"role":{"type":"string"}}}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}}},"/teams/{teamId}":{"patch":{"tags":["Teams"],"summary":"Update a team","operationId":"updateTeam","parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dryRun","in":"query","description":"Validate and authorize without writing. Returns a preview/impact body.","schema":{"type":"boolean"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamUpdate"}}}},"responses":{"200":{"description":"The updated team","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Team"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Teams"],"summary":"Delete a team","description":"Soft-deletes the team and cascades to its tasks, submissions, members and staff. Learners are left enrolled in the cohort but on no team — check `?dryRun=true` first.","operationId":"deleteTeam","parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dryRun","in":"query","description":"Validate and authorize without writing. Returns a preview/impact body.","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Deleted, with who was on the team","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"deleted":{"type":"object","properties":{"learners":{"type":"integer"},"staff":{"type":"integer"}}},"impact":{"type":"object"}}}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/teams/{teamId}/members":{"post":{"tags":["Members"],"summary":"Add a learner or staff member to a team","description":"One endpoint, two rosters: `role` picks between `team_members` (learner) and `team_staff_assignments` (mentor/evaluator). Staff are enrolled in the cohort at that role first, since a staff assignment on a non-member is rejected.","operationId":"addTeamMember","parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dryRun","in":"query","description":"Validate and authorize without writing. Returns a preview/impact body.","schema":{"type":"boolean"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamMemberInput"}}}},"responses":{"200":{"description":"Provisioning summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisioningSummary"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}},"patch":{"tags":["Members"],"summary":"Change a person's role on this team","description":"Team role, not cohort role. Today a team role must still match the cohort role, so a request that would violate that returns `409` with `details.reason: \"role_locked_to_cohort\"`.","operationId":"changeTeamMemberRole","parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dryRun","in":"query","description":"Validate and authorize without writing. Returns a preview/impact body.","schema":{"type":"boolean"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberRoleChange"}}}},"responses":{"200":{"description":"The new team role","content":{"application/json":{"schema":{"type":"object","properties":{"userId":{"type":"string"},"teamId":{"type":"string"},"teamRole":{"type":"string"}}}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"}}},"delete":{"tags":["Members"],"summary":"Remove a person from a team","description":"Removes whichever seat they hold — staff assignment or learner row. Their cohort enrolment is untouched.","operationId":"removeTeamMember","parameters":[{"name":"teamId","in":"path","required":true,"schema":{"type":"string"}},{"name":"dryRun","in":"query","description":"Validate and authorize without writing. Returns a preview/impact body.","schema":{"type":"boolean"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberEmail"}}}},"responses":{"200":{"description":"Removed","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"userId":{"type":"string"},"removed":{"type":"string","enum":["learner","staff"]}}}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/cohorts/{cohortId}/milestones":{"get":{"tags":["Milestones"],"summary":"List milestones in a cohort","operationId":"listCohortMilestones","parameters":[{"name":"cohortId","in":"path","required":true,"schema":{"type":"string"}},{"name":"q","in":"query","description":"Free-text search (matches name/title).","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size. Defaults to 50, clamped to a max of 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Opaque cursor from a previous page’s `nextCursor`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Milestones","content":{"application/json":{"schema":{"type":"object","properties":{"milestones":{"type":"array","items":{"$ref":"#/components/schemas/Milestone"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page; null when there are no more results."}}}}}}}}},"/cohorts/{cohortId}/tasks":{"get":{"tags":["Tasks"],"summary":"List tasks in a cohort","operationId":"listCohortTasks","parameters":[{"name":"cohortId","in":"path","required":true,"schema":{"type":"string"}},{"name":"milestoneId","in":"query","schema":{"type":"string"}},{"name":"assignee","in":"query","description":"Within the caller's team(s): 'me' = assigned to you, 'others' = assigned to teammates. Omit for all team tasks.","schema":{"type":"string","enum":["me","others"]}},{"name":"q","in":"query","description":"Free-text search (matches name/title).","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size. Defaults to 50, clamped to a max of 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Opaque cursor from a previous page’s `nextCursor`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Tasks","content":{"application/json":{"schema":{"type":"object","properties":{"tasks":{"type":"array","items":{"$ref":"#/components/schemas/Task"}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page; null when there are no more results."}}}}}}}}},"/milestones/{ref}/submissions":{"get":{"tags":["Milestones"],"summary":"List milestone submissions","operationId":"listMilestoneSubmissions","parameters":[{"name":"ref","in":"path","required":true,"schema":{"type":"string"},"description":"Milestone ref (e.g. T12M3) or id"},{"name":"q","in":"query","description":"Free-text search (matches name/title).","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size. Defaults to 50, clamped to a max of 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Opaque cursor from a previous page’s `nextCursor`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Submissions newest first","content":{"application/json":{"schema":{"type":"object","properties":{"submissions":{"type":"array","items":{}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page; null when there are no more results."}}}}}}}},"post":{"tags":["Milestones"],"summary":"Submit or resubmit a milestone","operationId":"submitMilestone","parameters":[{"name":"ref","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MilestoneSubmissionInput"}}}},"responses":{"200":{"description":"Created submission","content":{"application/json":{"schema":{"type":"object","properties":{"submission":{}}}}}},"409":{"description":"ambiguous_team — pass teamId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"422":{"description":"Validation / window error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}},"/tasks/{ref}/submissions":{"get":{"tags":["Tasks"],"summary":"List task submissions","operationId":"listTaskSubmissions","parameters":[{"name":"ref","in":"path","required":true,"schema":{"type":"string"},"description":"Task ref (e.g. TS42) or id"},{"name":"q","in":"query","description":"Free-text search (matches name/title).","schema":{"type":"string"}},{"name":"limit","in":"query","description":"Page size. Defaults to 50, clamped to a max of 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"cursor","in":"query","description":"Opaque cursor from a previous page’s `nextCursor`.","schema":{"type":"string"}}],"responses":{"200":{"description":"Submissions newest first","content":{"application/json":{"schema":{"type":"object","properties":{"submissions":{"type":"array","items":{}},"nextCursor":{"type":"string","nullable":true,"description":"Cursor for the next page; null when there are no more results."}}}}}}}},"post":{"tags":["Tasks"],"summary":"Submit or resubmit a task","operationId":"submitTask","parameters":[{"name":"ref","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskSubmissionInput"}}}},"responses":{"200":{"description":"Created submission","content":{"application/json":{"schema":{"type":"object","properties":{"submission":{}}}}}}}}},"/uploads":{"post":{"tags":["Uploads"],"summary":"Presign a file upload","operationId":"presignUpload","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadRequest"}}}},"responses":{"200":{"description":"Presigned PUT URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResult"}}}}}}}}}