openapi: 3.1.0 info: title: px0 API description: | px[0] is an open-source prompt infrastructure toolkit for managing prompts in production. It replaces hardcoded prompt strings with versioned templates, in-process caching, and OpenTelemetry observability, so teams can iterate on prompts without touching application code. This is the OpenAPI specification for all the public APIs of px0. version: 1.0.0 servers: - url: http://localhost:3000 description: Local development server paths: /v1/health: get: summary: Health Check description: Verifies that the service is running. operationId: healthCheck tags: - Health responses: '200': description: Service is healthy. content: application/json: schema: type: object properties: status: type: string example: OK required: - status /v1/search: get: summary: Search Registry Entities description: Searches prompts, skills, and tools across every project the requester can view. The server retrieves lexical and semantic candidates from the natural-language query, reranks them, and reapplies project access before returning common entity metadata. operationId: searchRegistry tags: - Search security: - BearerAuth: [] parameters: - name: q in: query required: true description: Natural-language text describing the desired prompt, skill, or tool. Can optionally include an inline type filter prefix or suffix (e.g., `type:prompt refund` or `refund type:tool`) which restricts results to the specified entity type. If an invalid type filter (e.g., `type:unknown`) is provided in the query string, a 400 Bad Request is returned. Provider names, modes, scores, and embedding vectors are internal and cannot be supplied by clients. schema: type: string minLength: 1 example: Find something that handles customer refunds - name: type in: query required: false description: Restricts the search to one entity type. When omitted, all registered entity types are searched together. schema: $ref: '#/components/schemas/SearchEntityType' x-edge-cases: - Missing, empty, or whitespace-only q returns 400 and never runs an unbounded search. - An unsupported type returns 400 with the complete list of accepted values. - Omitting type searches prompts, skills, and tools and returns one globally reranked result list. - An inline type filter in the q query parameter (e.g. `type:prompt`, `type:skill`, `type:tool`) restricts the search to that entity type, overriding the query parameter `type`. The filter prefix/suffix itself is stripped from the search text. - An invalid inline type filter (e.g. `type:unknown`) in the q query parameter returns a 400 Bad Request. - Every retriever receives only project IDs the requester can view; hydration reapplies the same scope as a defense-in-depth boundary. - Archived prompts are excluded. Skills and tools currently have no container archive state. - A project grant makes its entities searchable by the granted team's API keys; revocation removes them immediately. - Provider-specific scores, selection, and vectors are never exposed in the public request or response. x-test-coverage: - 'TestSearchAcrossAllEntityTypesAndAccessibleProjects: Verifies unfiltered search returns prompts, skills, and tools while excluding an inaccessible project.' - 'TestSearchFiltersByEntityType: Verifies type=tool returns only matching tools.' - 'TestSearchRejectsMissingQueryAndInvalidType: Verifies missing/blank q and unknown types return 400.' - 'TestSearchExcludesArchivedPrompts: Verifies archived prompt containers are not searchable.' - 'TestSearchRequiresAuthentication: Verifies unauthenticated requests return 401.' - 'TestSearchInlineTypeFilters: Verifies correct functioning of valid and invalid inline type filters.' - 'TestAPIKey_ReachesGrantedProject: Verifies search honors API-key project grants and revocations.' - 'TestPostgresRetrieverSearchesAllEntityTypesAndEnforcesScope: Verifies PostgreSQL FTS covers all current registry entities with project isolation.' - 'TestPostgresRetrieverHonorsEntityTypeFilter: Verifies the retriever applies the requested entity type.' - 'TestPostgresRetrieverReflectsEntityUpdates: Verifies generated search documents update transactionally with entity metadata.' - 'TestGetSearchResultsPreservesRankAndEnforcesScope: Verifies hydration preserves fused rank while dropping archived or unauthorized references.' - 'TestEngineSearchRetrievesAndReranksBothSources: Verifies lexical and semantic candidates are deterministically fused.' responses: '200': description: Ranked registry search results content: application/json: schema: $ref: '#/components/schemas/SearchResponse' '400': description: Missing query or unsupported entity type content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '500': description: Search provider or internal failure content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/auth/register: post: summary: Register a new user description: Registers a new user with an email and password. This endpoint can be called unauthenticated to register a new admin, or authenticated (as an admin) to register a standard user into a specific team. operationId: register tags: - Auth security: - {} - BearerAuth: [] x-edge-cases: - Fails with 400 Bad Request if email or password are empty, password is shorter than 8 characters, or password does not meet complexity requirements. - Fails with 400 Bad Request if email format is invalid. - Fails with 409 Conflict if email is already registered. - If admin registers a user (authenticated with Bearer token), they can optionally pass team_id to join an existing team (which must belong to an organization that the admin belongs to). If team_id is not passed, a Default Org and Default Team are created automatically. - If public user registers (unauthenticated), team_id is forbidden. - If Bearer token is provided but is invalid, expired, unverified, or not an admin, returns 401/403 appropriately. x-test-coverage: - 'TestRegister_Success: Verifies standard register returns 201 and User model.' - 'TestRegister_EmptyFields: Verifies empty fields reject with 400 and ''email and password are required''.' - 'TestRegister_ShortPassword: Verifies password shorter than 8 chars rejects with 400 and ''password must be at least 8 characters''.' - 'TestRegister_InvalidEmail: Verifies that invalid email format rejects with 400.' - 'TestRegister_WeakPassword: Verifies that a password without required complexity rejects with 400.' - 'TestRegister_DuplicateEmail: Verifies registering an existing email rejects with 409 and ''email already registered''.' - 'TestRegister_AdminSuccess: Verifies that an admin can successfully register a user into their organization''s team.' - 'TestRegister_AdminInvalidTeam: Verifies 404 if the specified team_id does not exist.' - 'TestRegister_AdminTeamNoOrg: Verifies 400 if the specified team does not belong to any organization.' - 'TestRegister_AdminDifferentOrg: Verifies 403 if the admin caller does not belong to the same organization as the specified team.' - 'TestRegister_PublicForbiddenTeamID: Verifies 403 if a public (unauthenticated) call attempts to pass a team_id.' - 'TestRegister_InvalidToken: Verifies 401 if an invalid Authorization header is provided.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RegisterRequest' responses: '201': description: User registered successfully content: application/json: schema: type: object properties: user: $ref: '#/components/schemas/User' required: - user '400': description: Invalid inputs content: application/json: schema: $ref: '#/components/schemas/APIError' examples: invalid_body: summary: Invalid JSON body structure value: error: invalid request body missing_fields: summary: Missing email or password value: error: email and password are required short_password: summary: Password is less than 8 characters long value: error: password must be at least 8 characters invalid_email: summary: Invalid email format value: error: invalid email format weak_password: summary: Password lacks complexity value: error: password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character team_no_org: summary: Specified team does not belong to any organization value: error: team does not belong to any organization '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' examples: only_admins_team: summary: Public register with team_id value: error: only admins can register users with a team_id different_org: summary: Admin registering user to different organization value: error: user does not belong to the organization of the specified team user_not_verified: summary: Caller is not verified value: error: user is not verified forbidden_caller: summary: Caller is not an admin value: error: forbidden '404': description: Team Not Found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: team not found '409': description: Conflict (Email registered) content: application/json: schema: $ref: '#/components/schemas/APIError' examples: duplicate_email: summary: Email is already in use value: error: email already registered '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: internal error /v1/auth/login: post: summary: Login description: Authenticates a user and creates a new access token. operationId: login tags: - Auth x-edge-cases: - Accepts JSON body. Session duration is configurable via SESSION_DURATION_HOURS environment variable (defaults to 24 hours). x-test-coverage: - 'TestLogin_Success: Verifies successful authentication and returns a token and expiry.' - 'TestLogin_InvalidCredentials: Verifies that incorrect passwords or emails reject with 401 and ''invalid credentials''.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LoginRequest' responses: '200': description: Login successful content: application/json: schema: type: object properties: token: type: string description: The access token to be used in standard Bearer authentication. example: f47ac10b-58cc-4372-a567-0e02b2c3d479 expires_at: type: string format: date-time description: The timestamp when this access token expires. user: $ref: '#/components/schemas/User' required: - token - expires_at - user '400': description: Invalid request body content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid request body '401': description: Invalid credentials content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid credentials '403': description: User is not verified content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: user is not verified '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: internal error /v1/auth/verify-email: post: summary: Verify User Email description: Verifies a user's email address using a numeric code sent via email. operationId: verifyEmail tags: - Auth x-edge-cases: - Fails with 400 Bad Request if verification code is invalid or expired. x-test-coverage: - 'TestRegister_AndVerifyFlow: Verifies the complete user verification flow.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/VerifyRequest' responses: '200': description: Email verified successfully content: application/json: schema: type: object properties: message: type: string example: email verified successfully required: - message '400': description: Invalid code or expired code content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid verification code '401': description: User not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid credentials '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: internal error get: summary: Trigger Verification Email description: Triggers a new email verification code and sends it to the user's email. operationId: triggerVerificationEmail tags: - Auth parameters: - name: email in: query required: true schema: type: string format: email description: The user's email address to trigger verification for. x-edge-cases: - Fails with 400 Bad Request if email query parameter is missing. - Fails with 400 Bad Request if user is already verified. - Fails with 404 Not Found if user is not found. x-test-coverage: - 'TestTriggerVerification: Verifies triggering a verification email.' responses: '200': description: Verification email sent successfully content: application/json: schema: type: object properties: message: type: string example: verification email sent successfully required: - message '400': description: Missing email, invalid email, or user already verified content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: email is required '404': description: User not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: user not found '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: internal error /v1/auth/session: delete: summary: Logout description: Destroys the active access token, logging the user out. operationId: logout tags: - Auth security: - BearerAuth: [] x-edge-cases: - If Bearer token is missing, the route returns 204 directly without throwing errors. x-test-coverage: - 'TestLogout_Success: Verifies standard 204 response and session deletion.' responses: '204': description: Logged out successfully (No content) /v1/auth/sessions: get: summary: List Active Sessions description: Returns a list of all active login sessions for the currently authenticated user. operationId: listSessions tags: - Auth security: - BearerAuth: [] x-edge-cases: - Requires a valid session token. x-test-coverage: - 'TestListSessions_Success: Verifies list of active sessions is returned.' responses: '200': description: List of active sessions content: application/json: schema: type: object properties: sessions: type: array items: $ref: '#/components/schemas/Session' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/auth/sessions/{sessionID}: delete: summary: Revoke Session description: Terminates and deletes a specific login session by ID. operationId: revokeSession tags: - Auth security: - BearerAuth: [] parameters: - name: sessionID in: path required: true schema: type: string format: uuid description: The ID of the session to terminate. x-edge-cases: - Requires a valid session token. - Users can only delete their own sessions. x-test-coverage: - 'TestRevokeSession_Success: Verifies a user can terminate another session of theirs.' responses: '204': description: Session terminated successfully '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Session not found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/auth/me: get: summary: Me description: Returns the profile of the currently logged-in user. operationId: me tags: - Profile security: - BearerAuth: [] x-test-coverage: - 'TestMe_WithSession: Verifies self profile lookup.' responses: '200': description: User profile content: application/json: schema: type: object properties: user: $ref: '#/components/schemas/User' required: - user '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized put: summary: Update Me description: Updates the profile of the currently logged-in user. operationId: updateMe tags: - Profile security: - BearerAuth: [] x-test-coverage: - 'TestUpdateMe: Verifies that updating user profile succeeds.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateMeRequest' responses: '200': description: User profile updated successfully content: application/json: schema: type: object properties: user: $ref: '#/components/schemas/User' required: - user '400': description: Invalid email format or missing fields content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '409': description: Email already taken content: application/json: schema: $ref: '#/components/schemas/APIError' delete: summary: Delete Me description: Deletes the currently logged-in user's account. operationId: deleteMe tags: - Profile security: - BearerAuth: [] x-test-coverage: - 'TestDeleteMe: Verifies that deleting a user account succeeds.' responses: '204': description: Account deleted successfully '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/auth/me/change-password: post: summary: Change Password description: Changes the currently logged-in user's password. operationId: changePassword tags: - Profile security: - BearerAuth: [] x-test-coverage: - 'TestChangePassword: Verifies changing authenticated password.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ChangePasswordRequest' responses: '200': description: Password changed successfully content: application/json: schema: type: object properties: message: type: string example: password changed successfully required: - message '400': description: Password too short or too weak content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized or invalid current password content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/auth/password-reset/trigger: post: summary: Trigger Password Reset description: Generates a password reset code and sends it via email to the user. operationId: triggerPasswordReset tags: - Auth x-edge-cases: - Fails with 400 Bad Request if email is missing. - Fails with 404 Not Found if user with specified email does not exist. x-test-coverage: - 'TestPasswordReset_Flow: Verifies triggering password reset and completing it successfully.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/TriggerPasswordResetRequest' responses: '200': description: Password reset email sent successfully content: application/json: schema: type: object properties: message: type: string example: password reset email sent successfully required: - message '400': description: Missing email content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: email is required '404': description: User not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: user not found '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: internal error /v1/auth/password-reset/reset: post: summary: Reset Password description: Resets a user's password using a valid reset code and a new password, deriving the user from the code in the database. operationId: resetPassword tags: - Auth x-edge-cases: - Fails with 400 Bad Request if code or new password is empty. - Fails with 400 Bad Request if new password is too short or too weak. - Fails with 400 Bad Request if reset code is invalid or expired. x-test-coverage: - 'TestPasswordReset_Flow: Verifies complete password reset flow.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ResetPasswordRequest' responses: '200': description: Password reset successfully content: application/json: schema: type: object properties: message: type: string example: password reset successfully email: type: string format: email example: user@example.com required: - message - email '400': description: Invalid input or invalid/expired code content: application/json: schema: $ref: '#/components/schemas/APIError' examples: missing_fields: summary: Missing code or password value: error: code and new_password are required short_password: summary: Password too short value: error: password must be at least 8 characters weak_password: summary: Password lacks complexity value: error: password must contain at least one uppercase letter, one lowercase letter, one digit, and one special character invalid_code: summary: Reset code is invalid or expired value: error: invalid or expired password reset code '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: internal error /v1/api-keys: post: summary: Create an API Key description: | Generates a new programmatic API key for accessing authorized resources. The full, secret API key is returned ONLY once in this response and cannot be recovered later. operationId: createAPIKey tags: - API Keys security: - BearerAuth: [] x-edge-cases: - Only standard user session tokens can create API keys; existing API keys cannot create or rotate other keys. - If team_ids is omitted or empty, the key is scoped to all teams in the organization. - Every supplied team_id must exist and belong to the specified org_id. x-test-coverage: - 'TestCreateAPIKey_Success: Verifies key creation with an explicit team scope.' - 'TestCreateAPIKey_MissingName: Verifies missing key name returns 400.' - 'TestCreateAPIKey_RequiresAccessToken: Verifies API keys cannot create other API keys.' - 'TestCreateAPIKey_GlobalScope: Verifies empty team_ids creates an org-wide key.' - 'TestCreateAPIKey_AdminScope: Verifies admin operation keys can perform authorized admin actions.' - 'TestCreateAPIKey_RejectsTeamOutsideOrg: Verifies teams from another organization are rejected.' - 'TestCreateAPIKey_RejectsUnknownTeam: Verifies unknown team IDs are rejected.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateAPIKeyRequest' responses: '201': description: API key created successfully content: application/json: schema: $ref: '#/components/schemas/APIKeyCreatedResponse' '400': description: Invalid request content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Referenced team not found content: application/json: schema: $ref: '#/components/schemas/APIError' '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' get: summary: List API Keys description: Lists metadata for all programmatic API keys. Secret key values are omitted. operationId: listAPIKeys tags: - API Keys security: - BearerAuth: [] parameters: - name: org_id in: query required: true schema: type: string format: uuid responses: '200': description: List of API keys content: application/json: schema: type: object properties: api_keys: type: array items: $ref: '#/components/schemas/APIKey' required: - api_keys '400': description: Bad Request '401': description: Unauthorized '403': description: Forbidden '500': description: Internal Server Error /v1/api-keys/{id}: put: summary: Update an API Key description: Update the name, operation, or scoped teams of an existing API key. operationId: updateAPIKey tags: - API Keys security: - BearerAuth: [] parameters: - name: id in: path required: true description: The unique UUID of the API Key to update. schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateAPIKeyRequest' responses: '200': description: API key updated successfully content: application/json: schema: type: object properties: api_key: $ref: '#/components/schemas/APIKey' '400': description: Invalid request '401': description: Unauthorized '403': description: Forbidden '404': description: API key not found '500': description: Internal Server Error delete: summary: Delete an API Key description: Revokes and permanently deletes an API key by its unique UUID. operationId: deleteAPIKey tags: - API Keys security: - BearerAuth: [] parameters: - name: id in: path required: true description: The unique UUID of the API Key to delete. schema: type: string format: uuid responses: '204': description: API key successfully deleted '400': description: Invalid UUID format '401': description: Unauthorized '403': description: Forbidden '404': description: API key not found '500': description: Internal Server Error /v1/api-keys/{id}/regenerate: post: summary: Rotate/Regenerate an API Key description: Generates and returns a new secure raw key string for an existing API Key while keeping metadata, scopes, and association unchanged. operationId: regenerateAPIKey tags: - API Keys security: - BearerAuth: [] parameters: - name: id in: path required: true description: The unique UUID of the API Key to regenerate. schema: type: string format: uuid x-edge-cases: - Only Org Admins and system administrators can regenerate API keys. x-test-coverage: - 'TestRegenerateAPIKey: Verifies regeneration produces a new key.' responses: '200': description: API key regenerated successfully content: application/json: schema: $ref: '#/components/schemas/APIKeyCreatedResponse' '400': description: Invalid request '401': description: Unauthorized '403': description: Forbidden '404': description: API key not found '500': description: Internal Server Error /v1/cache/purge: post: summary: Purge Global Cache description: Clears all caches globally across the cluster. operationId: purgeGlobalCache tags: - Cache Purges security: - BearerAuth: [] responses: '200': description: Cache purged successfully content: application/json: schema: type: object properties: success: type: boolean message: type: string '401': description: Unauthorized /v1/prompts/{id}/cache/purge: post: summary: Purge Prompt Cache description: Clears the cache for a specific prompt by its ID. operationId: purgePromptCache tags: - Cache Purges security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Cache purged successfully '401': description: Unauthorized /v1/skills/{id}/cache/purge: post: summary: Purge Skill Cache description: Clears the cache for a specific skill by its ID. operationId: purgeSkillCache tags: - Cache Purges security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Cache purged successfully '401': description: Unauthorized /v1/tools/{id}/cache/purge: post: summary: Purge Tool Cache description: Clears the cache for a specific tool by its ID. operationId: purgeToolCache tags: - Cache Purges security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Cache purged successfully '401': description: Unauthorized /v1/me/teams: get: summary: List User Teams description: Returns a list of teams the authenticated user belongs to. operationId: listUserTeams tags: - Profile security: - BearerAuth: [] responses: '200': description: A list of teams content: application/json: schema: type: object properties: teams: type: array items: $ref: '#/components/schemas/Team' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/me/teams/{teamID}: delete: summary: Leave Team description: Allows the authenticated user to voluntarily leave a team they belong to. operationId: leaveTeam tags: - Teams security: - BearerAuth: [] parameters: - name: teamID in: path required: true schema: type: string format: uuid responses: '204': description: Left team successfully (No Content) '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Team not found or user not a member of the team content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/me/orgs: get: summary: List User Organizations description: Returns a list of organizations the authenticated user belongs to. operationId: listUserOrgs tags: - Profile security: - BearerAuth: [] responses: '200': description: A list of organizations with roles content: application/json: schema: type: object properties: organizations: type: array items: $ref: '#/components/schemas/OrganizationWithRole' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/me/inbox: get: summary: Get Admin Inbox description: Returns a list of pending join requests that the authenticated user is authorized to approve or reject. operationId: getAdminInbox tags: - Inbox security: - BearerAuth: [] x-edge-cases: - Only returns pending join requests for teams where the user is an admin or is a system admin. - Returns a generic inbox item containing the `type` property as 'join_request' and an embedded `team` object. x-test-coverage: - 'TestJoinRequestsFlow: Verifies that the created team join request is properly listed in the admin''s inbox, showing correct embedded team details and user email.' responses: '200': description: A list of pending join requests content: application/json: schema: type: object properties: inbox: type: array items: $ref: '#/components/schemas/InboxItem' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/orgs/{orgID}/teams: post: summary: Create Team description: Creates a new team under a specific organization. Requires Org Admin privileges (admin on the Default Team). Team admins or editors of other custom teams are not authorized to create teams. operationId: createTeam tags: - Team Management security: - BearerAuth: [] x-edge-cases: - Requires Org Admin privileges (admin on the Default Team). - Team admins or editors of other custom teams are forbidden from creating teams. - Fails with 400 if name is empty. - Fails with 409 if the team name already exists under the target organization. - The team creator is automatically and atomically added as an admin member of the newly created team. x-test-coverage: - 'TestRolesAndPermissions: Verifies that a Team Admin of a custom team is forbidden from creating a team (returns 403 Forbidden).' - 'TestRolesAndPermissions: Verifies that an Org Admin (admin of Default Team) can create a team (returns 201 Created).' - 'TestCreateTeam_Success: Verifies that a team is successfully created and the creator is automatically assigned as an admin.' parameters: - name: orgID in: path required: true schema: type: string format: uuid description: The ID of the organization to create the team under. requestBody: required: true content: application/json: schema: type: object required: - name properties: name: type: string example: Engineering responses: '201': description: Created content: application/json: schema: type: object properties: team: $ref: '#/components/schemas/Team' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' get: summary: List Org Teams description: Returns a list of all teams within the specified organization. operationId: listOrgTeams tags: - Team Management security: - BearerAuth: [] parameters: - name: orgID in: path required: true schema: type: string format: uuid description: The ID of the organization to list teams for. responses: '200': description: A list of teams content: application/json: schema: type: object properties: teams: type: array items: $ref: '#/components/schemas/Team' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/teams/{id}: put: summary: Update Team description: Updates an existing team. Requires Org Admin, Team Admin, or Team Editor privileges. Team Members (viewers) are not authorized. operationId: updateTeam tags: - Team Management security: - BearerAuth: [] x-edge-cases: - Requires Org Admin, Team Admin, or Team Editor privileges. - Team Members (viewers) are forbidden from updating team details. - Returns 404 if the team does not exist. - Returns 409 if the updated team name already exists under the target organization. x-test-coverage: - 'TestRolesAndPermissions: Verifies that a Viewer (Team Member) cannot update a team (returns 403 Forbidden).' - 'TestRolesAndPermissions: Verifies that an Editor (Team Editor) can update a team (returns 200 OK).' - 'TestRolesAndPermissions: Verifies that an Admin (Team Admin) can update a team (returns 200 OK).' parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: type: object required: - name properties: name: type: string example: Engineering org_id: type: string format: uuid example: f47ac10b-58cc-4372-a567-0e02b2c3d479 responses: '200': description: Updated content: application/json: schema: type: object properties: team: $ref: '#/components/schemas/Team' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Not Found content: application/json: schema: $ref: '#/components/schemas/APIError' delete: summary: Delete Team description: Deletes an existing team. Requires Org Admin, Team Admin, or Team Editor privileges. Team Members (viewers) are not authorized. operationId: deleteTeam tags: - Team Management security: - BearerAuth: [] x-edge-cases: - Requires Org Admin, Team Admin, or Team Editor privileges. - Team Members (viewers) are forbidden from deleting the team. - Returns 404 if the team does not exist. x-test-coverage: - 'TestRolesAndPermissions: Verifies that a Viewer (Team Member) cannot delete a team (returns 403 Forbidden).' - 'TestRolesAndPermissions: Verifies that an Editor (Team Editor) can delete a team (returns 204 No Content).' - 'TestRolesAndPermissions: Verifies that an Admin (Team Admin) can delete a team (returns 204 No Content).' parameters: - name: id in: path required: true schema: type: string format: uuid responses: '204': description: Team deleted successfully '401': description: Unauthorized '403': description: Forbidden '404': description: Not Found /v1/teams/{id}/members: get: summary: List Team Members description: Returns a paginated list of members for a given team. Requires at least viewer access. operationId: listTeamMembers tags: - User Management security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid - name: page in: query required: false schema: type: integer default: 1 responses: '200': description: Paginated list of members content: application/json: schema: type: object properties: members: type: array items: $ref: '#/components/schemas/TeamMemberResponse' page: type: integer limit: type: integer total: type: integer '400': description: Bad Request '401': description: Unauthorized '403': description: Forbidden post: summary: Add Team Member description: Adds a user to a team. Requires admin privileges. operationId: addTeamMember tags: - User Management security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: type: object required: - user_id properties: user_id: type: string format: uuid responses: '204': description: No Content '400': description: Bad Request '401': description: Unauthorized '403': description: Forbidden /v1/teams/{id}/members/{userID}: delete: summary: Remove Team Member description: Removes a user from a team. Requires admin privileges. operationId: removeTeamMember tags: - User Management security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid - name: userID in: path required: true schema: type: string format: uuid responses: '204': description: No Content '400': description: Bad Request '401': description: Unauthorized '403': description: Forbidden /v1/teams/{id}/members/{userID}/role: put: summary: Update Team Member Role description: Updates a team member's role. Requires team admin privileges. operationId: updateTeamMemberRole tags: - User Management security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid - name: userID in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: type: object required: - role properties: role: type: string enum: - admin - editor - viewer example: admin responses: '200': description: Role updated successfully content: application/json: schema: type: object properties: message: type: string '400': description: Bad Request '401': description: Unauthorized '403': description: Forbidden '404': description: Not Found /v1/teams/{id}/join-requests: post: summary: Request to Join Team description: Creates a pending request for the authenticated user to join a specific team. operationId: createJoinRequest tags: - Teams security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid description: The ID of the team to request to join. responses: '201': description: Join request created successfully content: application/json: schema: $ref: '#/components/schemas/TeamJoinRequest' '400': description: Bad Request (already a member) content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Team Not Found content: application/json: schema: $ref: '#/components/schemas/APIError' '409': description: Conflict (already has a pending request) content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/join-requests/{id}: put: summary: Resolve Join Request description: Approves or rejects a pending join request. operationId: resolveJoinRequest tags: - Inbox security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid description: The ID of the join request to resolve. requestBody: required: true content: application/json: schema: type: object required: - status properties: status: type: string enum: - approved - rejected example: approved x-edge-cases: - Only authorized admins of the team can approve or reject the request. - Join request must be in pending status to be resolved. x-test-coverage: - 'TestJoinRequestsFlow: Verifies that a join request can be approved, updating the user''s role and membership.' responses: '200': description: Resolved request details content: application/json: schema: $ref: '#/components/schemas/TeamJoinRequest' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden (not authorized to approve) content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Join Request Not Found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/orgs: post: summary: Create Organization description: Creates a new organization. Requires admin privileges. operationId: createOrg tags: - Organizations security: - BearerAuth: [] requestBody: required: true content: application/json: schema: type: object required: - name properties: name: type: string example: Acme Corp responses: '201': description: Created content: application/json: schema: type: object properties: org: $ref: '#/components/schemas/Organization' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/orgs/{id}: put: summary: Update Organization description: Updates an existing organization's metadata. Requires admin privileges. operationId: updateOrg tags: - Organizations security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: type: object required: - name properties: name: type: string example: Acme Industries responses: '200': description: Updated successfully content: application/json: schema: type: object properties: org: $ref: '#/components/schemas/Organization' '400': description: Bad Request content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Not Found content: application/json: schema: $ref: '#/components/schemas/APIError' delete: summary: Delete Organization description: Deletes an organization and all cascading dependencies. Requires Org Admin. operationId: deleteOrg tags: - Organizations security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid responses: '204': description: Deleted successfully (No Content) '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Not Found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/orgs/{orgID}/people: get: summary: List Org People description: Returns a paginated list of distinct people who are members of any team in the organization. operationId: listOrgPeople tags: - Organizations security: - BearerAuth: [] parameters: - name: orgID in: path required: true schema: type: string format: uuid description: The ID of the organization to list people for. - name: page in: query required: false schema: type: integer default: 1 - name: limit in: query required: false schema: type: integer default: 10 responses: '200': description: Paginated list of people content: application/json: schema: type: object required: - people - page - limit - total properties: people: type: array items: $ref: '#/components/schemas/User' page: type: integer limit: type: integer total: type: integer '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Organization Not Found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/orgs/{orgID}/members/{userID}: delete: summary: Remove Member from Organization description: Removes a user from an organization by removing them from all teams in that organization. Requires Org Admin privileges. operationId: removeOrgMember tags: - Organizations security: - BearerAuth: [] x-edge-cases: - Only Org Admins and system administrators can remove members from an organization. - Returns 403 Forbidden if a standard user tries to remove a member. - Returns 404 Not Found if the user is not a member of the organization. x-test-coverage: - 'TestOrg_RemoveMember: Verifies that an Org Admin can successfully remove a member from the organization, removing them from all teams.' - 'TestOrg_RemoveMember: Verifies that a standard user cannot remove a member (returns 403 Forbidden).' - 'TestOrg_RemoveMember: Verifies that removing a user who is not a member of the organization returns 404 Not Found.' parameters: - name: orgID in: path required: true schema: type: string format: uuid description: The ID of the organization. - name: userID in: path required: true schema: type: string format: uuid description: The ID of the user to remove from the organization. responses: '204': description: User successfully removed from the organization '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Not Found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/orgs/{orgID}/members/{userID}/role: put: summary: Update Organization Member Role description: | Promotes a standard user to an organization admin role or demotes them back to a standard member. Requires Org Admin privileges. operationId: updateOrgMemberRole tags: - Organizations security: - BearerAuth: [] parameters: - name: orgID in: path required: true schema: type: string format: uuid description: The ID of the organization. - name: userID in: path required: true schema: type: string format: uuid description: The ID of the user whose role is being updated. x-edge-cases: - Only Org Admins and system administrators can update member roles. - Returns 403 Forbidden if a standard user tries to update a member's role. - Returns 404 Not Found if the user is not a member of the organization. x-test-coverage: - 'TestOrg_UpdateMemberRole: Verifies that an Org Admin can successfully promote and demote a member.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateOrgMemberRoleRequest' responses: '200': description: Role updated successfully content: application/json: schema: type: object properties: success: type: boolean '400': description: Invalid request content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: User or Organization Not Found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/orgs/{orgID}/audit-logs: get: summary: List Administrative Audit Logs description: | Returns a paginated event stream of administrative actions in the organization. Requires Org Admin privileges. operationId: listOrgAuditLogs tags: - Organizations security: - BearerAuth: [] parameters: - name: orgID in: path required: true schema: type: string format: uuid description: The ID of the organization. - name: limit in: query schema: type: integer default: 50 - name: offset in: query schema: type: integer default: 0 x-edge-cases: - Only Org Admins and system administrators can view audit logs. - Returns 403 Forbidden if a standard user tries to view audit logs. x-test-coverage: - 'TestOrg_ListAuditLogs: Verifies that an Org Admin can successfully view audit logs, while a standard user cannot.' responses: '200': description: A paginated list of audit logs content: application/json: schema: type: object properties: logs: type: array items: $ref: '#/components/schemas/AuditLog' '400': description: Invalid request content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Organization Not Found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/projects: post: summary: Create a Project description: | Creates a project owned by a team. The requester must be an editor-or-above member of the owning team, or an admin of that team's organization. The project name and slug must each be unique within the owning team. operationId: createProject tags: - Projects security: - BearerAuth: [] x-edge-cases: - A viewer of the owning team, or a non-member, is refused with 403. - An org admin can create a project for any team in their org without being a member of that team. - A duplicate name or slug within the same owning team returns 409; the same name/slug under a different team is allowed. - If slug is omitted it is derived from the name and normalized. x-test-coverage: - 'TestCreateProject_Success: editor creates a project (201).' - 'TestCreateProject_ForbiddenForViewer: a viewer is refused (403).' - 'TestCreateProject_MissingName: missing name returns 400.' - 'TestCreateProject_Duplicate: duplicate name/slug in the same team returns 409.' - 'TestCreateProject_Unauthorized: unauthenticated request returns 401.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateProjectRequest' responses: '201': description: Project created successfully content: application/json: schema: $ref: '#/components/schemas/ProjectResponse' '400': description: Invalid request (invalid body, invalid team_id, or missing name) content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden (requester is not an editor of the team or an org admin) content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Owning team not found content: application/json: schema: $ref: '#/components/schemas/APIError' '409': description: A project with this name or slug already exists in the team content: application/json: schema: $ref: '#/components/schemas/APIError' '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/projects/{projectID}: put: summary: Update a Project description: | Updates a project's metadata (e.g., name and/or slug). The requester must be an editor-or-above of the owning team or an admin of its organization. operationId: updateProject tags: - Projects security: - BearerAuth: [] parameters: - name: projectID in: path required: true description: The ID of the project. schema: type: string format: uuid x-edge-cases: - A duplicate name or slug within the same owning team returns 409. - A non-editor member is refused with 403. - An unknown project id returns 404. x-test-coverage: - 'TestUpdateProject_Success: editor updates the project (200).' - 'TestUpdateProject_Duplicate: duplicate name/slug returns 409.' - 'TestUpdateProject_Forbidden: a viewer is refused (403).' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateProjectRequest' responses: '200': description: Project updated successfully content: application/json: schema: $ref: '#/components/schemas/ProjectResponse' '400': description: Invalid project id or request body content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden (requester is not an editor of the owning team or org admin) content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Project not found content: application/json: schema: $ref: '#/components/schemas/APIError' '409': description: Duplicate project name or slug in team content: application/json: schema: $ref: '#/components/schemas/APIError' '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' get: summary: Get a Project description: | Fetches a project by ID. The requester must be able to reach the project — as a member of the owning team or of a team granted access. Projects the requester cannot reach are reported as not found. operationId: getProject tags: - Projects security: - BearerAuth: [] parameters: - name: projectID in: path required: true description: The ID of the project. schema: type: string format: uuid x-edge-cases: - An unknown project id, or one the requester cannot reach, returns 404. x-test-coverage: - 'TestGetProject_Success: owning-team member fetches the project (200).' - 'TestGetProject_NotFoundForNonMember: a non-member gets 404.' - 'TestGetProject_NotFound: an unknown id returns 404.' responses: '200': description: The project content: application/json: schema: $ref: '#/components/schemas/ProjectResponse' '400': description: Invalid project id content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Project not found or not reachable by the requester content: application/json: schema: $ref: '#/components/schemas/APIError' '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' delete: summary: Delete a Project description: | Hard-deletes a project, cascading to its prompts (and their versions, tags, and payloads) and dropping its access grants. The requester must be an admin of the owning team or an admin of that team's organization. operationId: deleteProject tags: - Projects security: - BearerAuth: [] parameters: - name: projectID in: path required: true description: The ID of the project. schema: type: string format: uuid x-edge-cases: - A non-admin member of the owning team is refused with 403. - An unknown project id returns 404. x-test-coverage: - 'TestDeleteProject_Success: an owning-team admin deletes the project (204).' - 'TestDeleteProject_ForbiddenForNonAdmin: a non-admin is refused (403).' - 'TestDeleteProject_NotFound: an unknown id returns 404.' responses: '204': description: Project deleted successfully (No Content) '400': description: Invalid project id content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden (requester is not an admin of the owning team or org) content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Project not found content: application/json: schema: $ref: '#/components/schemas/APIError' '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/projects/{projectID}/access: post: summary: Grant Project Access description: | Grants another team access to the project's prompts. The requester must be an admin of the owning team or an admin of its organization. The grantee team must belong to the same organization as the owning team. An org-less owning team cannot share its projects. operationId: grantProjectAccess tags: - Projects security: - BearerAuth: [] parameters: - name: projectID in: path required: true description: The ID of the project. schema: type: string format: uuid x-edge-cases: - A non-admin of the owning team is refused with 403. - A grantee team in a different organization is refused with 403. - An org-less owning team cannot share; the grant is refused with 403. - Granting to the owning team is rejected with 400 (it already has implicit access). - Re-granting an existing access is idempotent and returns 201. - An unknown project returns 404; an unknown grantee team returns 404. x-test-coverage: - 'TestGrantProjectAccess_Success: owning-team admin grants same-org team (201) and the grantee reaches the project.' - 'TestGrantProjectAccess_ForbiddenForNonAdmin: an editor is refused (403).' - 'TestGrantProjectAccess_RejectsTeamOutsideOrg: a foreign-org grantee is refused (403).' - 'TestGrantProjectAccess_RejectsOrgLessOwner: an org-less owning team cannot share (403).' - 'TestGrantProjectAccess_UnknownProject: an unknown project returns 404.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/GrantProjectAccessRequest' responses: '201': description: Access granted content: application/json: schema: $ref: '#/components/schemas/ProjectAccessResponse' '400': description: Invalid request (invalid body/team_id, or grantee is the owning team) content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden (non-admin, org-less owner, or grantee outside org) content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Project or grantee team not found content: application/json: schema: $ref: '#/components/schemas/APIError' '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/projects/{projectID}/access/{teamID}: delete: summary: Revoke Project Access description: | Revokes a team's access to the project. The requester must be an admin of the owning team or an admin of its organization. The owning team's implicit access cannot be revoked through this endpoint. operationId: revokeProjectAccess tags: - Projects security: - BearerAuth: [] parameters: - name: projectID in: path required: true description: The ID of the project. schema: type: string format: uuid - name: teamID in: path required: true description: The ID of the team whose access is being revoked. schema: type: string format: uuid x-edge-cases: - A non-admin of the owning team is refused with 403. - Revoking an access that does not exist returns 404. - An unknown project returns 404. x-test-coverage: - 'TestRevokeProjectAccess_Success: an admin revokes access (204) and the grantee loses reachability.' - 'TestRevokeProjectAccess_ForbiddenForNonAdmin: an editor is refused (403).' - 'TestRevokeProjectAccess_NotFound: revoking a non-existent grant returns 404.' responses: '204': description: Access revoked successfully (No Content) '400': description: Invalid project or team id content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden (requester is not an admin of the owning team or org) content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Project or access grant not found content: application/json: schema: $ref: '#/components/schemas/APIError' '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/teams/{teamID}/projects: get: summary: List a Team's Projects description: | Lists the projects a team owns plus those granted to it. The requester must be a member of the team. operationId: listTeamProjects tags: - Projects security: - BearerAuth: [] parameters: - name: teamID in: path required: true description: The ID of the team. schema: type: string format: uuid x-edge-cases: - A non-member of the team is refused with 403. - A team owning and granted nothing returns an empty array. x-test-coverage: - 'TestListTeamProjects_Success: a member lists the team''s owned projects (200).' - 'TestListTeamProjects_ForbiddenForNonMember: a non-member gets 403.' responses: '200': description: List of projects content: application/json: schema: $ref: '#/components/schemas/ProjectListResponse' '400': description: Invalid team id content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden (requester is not a member of the team) content: application/json: schema: $ref: '#/components/schemas/APIError' '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/projects/{projectID}/prompts: post: summary: Create a Prompt description: Creates a new prompt inside a project. The requester must be an editor-or-above on the project (via the owning team or a granted team). operationId: createPrompt tags: - Prompts security: - BearerAuth: [] parameters: - name: projectID in: path required: true schema: type: string format: uuid description: The ID of the project. x-edge-cases: - A viewer of the project is refused with 403; an unknown project returns 404. - Slug uniqueness is scoped to the project, so the same slug may exist in another project. x-test-coverage: - 'TestCreatePrompt_Success: Verifies prompt creation and response payload.' - 'TestCreatePrompt_MissingName: Verifies that missing name returns 400 and ''name is required''.' - 'TestCreatePrompt_ForbiddenForViewer: Verifies a viewer cannot create a prompt (403).' - 'TestCreatePrompt_UnknownProject: Verifies an unknown project returns 404.' - 'TestCreatePrompt_Unauthorized: Verifies that unauthenticated request returns 401.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreatePromptRequest' responses: '201': description: Prompt created successfully content: application/json: schema: type: object properties: prompt: $ref: '#/components/schemas/Prompt' required: - prompt '400': description: Invalid request content: application/json: schema: $ref: '#/components/schemas/APIError' examples: invalid_body: value: error: invalid request body name_required: value: error: name is required '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '403': description: Forbidden (requester is not an editor of the project) content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Project not found content: application/json: schema: $ref: '#/components/schemas/APIError' '409': description: A prompt with this name or slug already exists in the project content: application/json: schema: $ref: '#/components/schemas/APIError' '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: internal error get: summary: List Prompts description: Lists the prompts inside a project. The requester must be able to reach the project. operationId: listPrompts tags: - Prompts security: - BearerAuth: [] parameters: - name: projectID in: path required: true schema: type: string format: uuid description: The ID of the project. - name: archived in: query required: false description: Optional boolean to filter prompts by archive state. schema: type: boolean x-edge-cases: - An API key reaches the prompts of every project its teams can access — owned or granted — and loses that reach when the grant is revoked. x-test-coverage: - 'TestListPrompts: Verifies listing populated prompt containers.' - 'TestListPrompts_Empty: Verifies that listing when empty returns an empty array.' - 'TestPrompts_APIKeyAuth: Verifies an API key lists a project owned by its team.' - 'TestAPIKey_ReachesGrantedProject: Verifies an API key reaches a granted project''s prompts and is denied after revoke.' - 'TestAPIKey_DeniedForeignProject: Verifies an API key is denied a project its teams cannot access.' responses: '200': description: List of prompts content: application/json: schema: type: object properties: prompts: type: array items: $ref: '#/components/schemas/Prompt' required: - prompts '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '403': description: Forbidden (requester cannot reach the project) content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Project not found content: application/json: schema: $ref: '#/components/schemas/APIError' '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: internal error /v1/prompts: get: summary: List all prompts with project filter description: Returns a list of prompts. If the project_id query parameter is not provided, returns an empty list by default. Users can filter by a project they can reach. operationId: listAllPrompts tags: - Prompts security: - BearerAuth: [] parameters: - name: project_id in: query required: false description: Optional UUID of the project to filter prompts (alias of project). schema: type: string format: uuid - name: project in: query required: false description: Optional UUID of the project to filter prompts (alias of project_id). schema: type: string format: uuid - name: archived in: query required: false description: Optional boolean to filter prompts by archive state. schema: type: boolean x-edge-cases: - By default with no project_id query parameter, returns an empty list of prompts. - If a project_id is provided, checks the requester can reach that project, otherwise returns 403 Forbidden. x-test-coverage: - 'TestListAllPrompts: Verifies that no project_id returns 200 with an empty list.' - 'TestListAllPrompts: Verifies that a valid project_id returns 200 with prompts of that project.' - 'TestListAllPrompts: Verifies that an unallowed project_id returns 403 Forbidden.' responses: '200': description: A list of prompts content: application/json: schema: type: object properties: prompts: type: array items: $ref: '#/components/schemas/Prompt' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '500': description: Internal Server Error content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}: get: summary: Get a Prompt description: Returns details of a specific prompt container by its unique UUID or unique slug. Optionally retrieves version or tag details if requested via query parameters. operationId: getPrompt tags: - Prompts security: - BearerAuth: [] parameters: - name: id in: path required: true description: The unique UUID or unique slug of the prompt. schema: type: string - name: version in: query required: false description: Optional version sequence number (integer) or version tag (string) to retrieve along with the prompt details. schema: type: string - name: tag in: query required: false description: Optional version tag (string) to retrieve along with the prompt details. schema: type: string x-test-coverage: - 'TestGetPrompt_Success: Verifies finding a prompt by ID.' - 'TestGetPrompt_BySlugAndVersion: Verifies finding a prompt by slug, with version, and with tag query parameter.' - 'TestGetPrompt_NotFound: Verifies 404 response on missing prompt ID.' responses: '200': description: Prompt details content: application/json: schema: type: object properties: prompt: $ref: '#/components/schemas/Prompt' version: $ref: '#/components/schemas/PromptVersion' required: - prompt '400': description: Invalid UUID format content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid prompt id '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '404': description: Prompt not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: prompt not found '500': description: Internal error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: internal error put: summary: Update a Prompt description: Updates the description of a specific prompt by its unique UUID. operationId: updatePrompt tags: - Prompts security: - BearerAuth: [] parameters: - name: id in: path required: true description: The unique UUID of the prompt. schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdatePromptRequest' x-edge-cases: - 'Forbidden: Attempting to update a prompt with viewer permissions.' - 'NotFound: Attempting to update a non-existent prompt ID or a prompt under an unauthorized team.' x-test-coverage: - 'TestUpdatePrompt_Success: Verifies updating description with editor token.' - 'TestUpdatePrompt_ViewerForbidden: Verifies viewer gets a 403 response.' - 'TestUpdatePrompt_NotFound: Verifies 404 response on missing prompt ID.' responses: '200': description: Prompt updated successfully content: application/json: schema: type: object properties: prompt: $ref: '#/components/schemas/Prompt' required: - prompt '400': description: Invalid input or invalid UUID format content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid request body '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '403': description: Forbidden - Viewer or unauthorized team member content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: forbidden '404': description: Prompt not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: prompt not found '500': description: Internal error content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}/schema: post: summary: Update Prompt Schema description: Stores or updates a JSON Schema at the prompt level to enforce presence and types of input variables before execution. operationId: updatePromptSchema tags: - Prompts security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid description: The ID of the prompt. requestBody: required: true content: application/json: schema: type: object properties: schema: type: object additionalProperties: true description: The JSON Schema to store. responses: '200': description: Schema updated successfully content: application/json: schema: type: object properties: prompt: $ref: '#/components/schemas/Prompt' required: - prompt '400': description: Invalid request content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt not found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}/archive: post: summary: Archive a Prompt description: Archives a specific prompt container, setting `status` to 'archived'. The prompt still exists in the system and people can call it and use it, so a prompt is never deleted. Requires Org Admin or Team Admin privileges (GitHub repo owner model). Team Editors and Team Members are not authorized to archive prompts. operationId: archivePrompt tags: - Prompts security: - BearerAuth: [] x-edge-cases: - Only Org Admins and Team Admins can archive a prompt. - Team Editors and Team Members (viewers) are forbidden from archiving prompts. x-test-coverage: - 'TestArchivePrompt_Permissions: Verifies that a Team Member (viewer) cannot archive a prompt (returns 403 Forbidden).' - 'TestArchivePrompt_Permissions: Verifies that a Team Editor cannot archive a prompt (returns 403 Forbidden).' - 'TestArchivePrompt_Permissions: Verifies that a Team Admin can successfully archive a prompt (returns 200 OK with prompt details).' parameters: - name: id in: path required: true description: The unique UUID of the prompt to archive. schema: type: string format: uuid responses: '200': description: Prompt successfully archived content: application/json: schema: type: object properties: prompt: $ref: '#/components/schemas/Prompt' required: - prompt '400': description: Invalid UUID format content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid prompt id '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: forbidden '404': description: Prompt not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: prompt not found '500': description: Internal error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: internal error /v1/prompts/{id}/restore: post: summary: Restore a Prompt description: Restores a previously archived prompt setting `status` back to 'active'. Requires Team Admin or Org Admin. operationId: restorePrompt tags: - Prompts security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid responses: '200': description: Prompt successfully restored content: application/json: schema: type: object properties: prompt: $ref: '#/components/schemas/Prompt' required: - prompt '400': description: Invalid UUID format content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt not found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}/rollback: post: summary: Rollback a Prompt description: Instantly demotes the active live version and promotes a target historical version in a single atomic transaction. operationId: rollbackPrompt tags: - Prompts security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: type: object required: - target_version - rollback_reason properties: target_version: type: integer description: The historical version number to promote to live. rollback_reason: type: string description: The reason for rolling back. responses: '200': description: Prompt successfully rolled back content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/PromptVersion' required: - version '400': description: Invalid UUID format or request body content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt or target version not found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}/move: post: summary: Move a Prompt description: Moves a prompt from its current project to another project. Requires admin capability on both the source and target project (owning-team or org admin). operationId: movePrompt tags: - Prompts security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid x-edge-cases: - Requires admin on both source and target project; a non-admin on either side is refused with 403. - A move into a project where the prompt's name or slug already exists is rejected with 409. - An unknown prompt or an unknown target project returns 404. x-test-coverage: - 'TestMovePrompt: Verifies an admin moves a prompt between projects (200).' - 'TestMovePrompt_TargetCollision: Verifies a name/slug collision in the target project returns 409.' requestBody: required: true content: application/json: schema: type: object required: - project_id properties: project_id: type: string format: uuid description: The target project to move the prompt into. responses: '200': description: Prompt successfully moved content: application/json: schema: type: object properties: prompt: $ref: '#/components/schemas/Prompt' required: - prompt '400': description: Invalid UUID format or project_id content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden (not an admin on the source or target project) content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt or target project not found content: application/json: schema: $ref: '#/components/schemas/APIError' '409': description: A prompt with this name or slug already exists in the target project content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}/versions/diff: get: summary: Diff Prompt Versions description: Compares the templates of two prompt versions side-by-side and returns a unified diff. Requires read access to the prompt. operationId: diffVersions tags: - Prompt Versions security: - BearerAuth: [] x-edge-cases: - Accepts either a version sequence number or a tag (e.g. 'prod', 'dev') for both 'from' and 'to' query parameters. - Returns 404 Not Found if either the 'from' or 'to' version/tag does not exist. - Fails with 400 Bad Request if 'from' or 'to' query parameters are missing. x-test-coverage: - 'TestDiffVersions: Verifies diffing via numeric sequence numbers, diffing via tags, and verifying a nonexistent tag returns 404.' parameters: - name: id in: path required: true schema: type: string format: uuid - name: from in: query required: true schema: type: string description: The source version number (integer) or version tag (string) to compare from. - name: to in: query required: true schema: type: string description: The target version number (integer) or version tag (string) to compare to. responses: '200': description: Versions compared successfully content: application/json: schema: type: object properties: from_version: type: integer example: 1 to_version: type: integer example: 2 from_template: type: string example: Hello {{ .name }} to_template: type: string example: |- Hello {{ .name }}! Welcome to px0. diff: type: string example: | --- v1 +++ v2 @@ -1 +1,2 @@ -Hello {{ .name }} +Hello {{ .name }}! +Welcome to px0. required: - from_version - to_version - from_template - to_template - diff '400': description: Missing or invalid parameters content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt or version not found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/projects/{projectID}/prompts/{slug}/render: post: summary: Render Live Prompt Version description: Renders the active 'live' template version of a prompt using supplied variables. The prompt is resolved by slug within the given project, so the same slug may be reused across projects. operationId: renderLive tags: - Prompt Renders security: - BearerAuth: [] parameters: - name: projectID in: path required: true description: The ID of the project the prompt belongs to. schema: type: string format: uuid - name: slug in: path required: true description: Prompt slug, unique within the project. schema: type: string x-edge-cases: - Fails with 404 if no live version is found. - Fails with 422 if template execution fails due to invalid parameters or template syntax mismatches. - Fails with 422 when the template references a variable that is not present in the request variables. x-test-coverage: - 'TestRenderLive_Success: Verifies rendering with complete variables returning 200 OK.' - 'TestRenderLive_NoLiveVersion: Verifies that requesting a render with only ''draft'' versions fails with 404 and ''no live version found for this prompt''.' - 'TestRenderLive_NoVariables: Verifies static templates render correctly when empty variables are supplied.' - 'TestRenderLive_MissingVariable: Verifies missing template variables fail with 422 instead of rendering placeholder text.' - 'TestRenderLive_IncludesModelConfig: Verifies render responses include the prompt version model and parameters.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RenderRequest' responses: '200': description: Rendered template string content: application/json: schema: $ref: '#/components/schemas/RenderResponse' '400': description: Invalid slug or bad body JSON content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid prompt slug '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '404': description: Prompt or live version not found content: application/json: schema: $ref: '#/components/schemas/APIError' examples: prompt_not_found: value: error: prompt not found no_live_version: value: error: no live version found for this prompt '422': description: Render execution failed content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: 'template execution failed: ...' '500': description: Internal error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: internal error /v1/projects/{projectID}/prompts/{slug}/run: post: summary: Run Live Prompt Version description: Renders the active 'live' template version and executes it against the configured model provider (e.g. OpenAI or Anthropic). Supports ad-hoc overrides for models and params, and unified stream mode. operationId: runLive tags: - Prompt Runs security: - BearerAuth: [] parameters: - name: projectID in: path required: true description: The ID of the project the prompt belongs to. schema: type: string format: uuid - name: slug in: path required: true description: Prompt slug, unique within the project. schema: type: string x-edge-cases: - Fails with 400 if provider key is missing. - Fails with 502 Bad Gateway if provider API returns non-200. x-test-coverage: - 'TestRunLive_Success_OpenAI_Block: Verifies block model completions for OpenAI.' - 'TestRunLive_Success_OpenAI_Stream: Verifies unified Server-Sent Events delta streams for OpenAI.' - 'TestRunLive_Success_Anthropic_Block: Verifies block completions for Anthropic.' - 'TestRunLive_Success_Anthropic_Stream: Verifies unified SSE streams for Anthropic.' - 'TestRunLive_AdhocOverrides: Verifies that model and model_params overrides take precedence.' - 'TestRunLive_ProviderErrorHandling: Verifies bad gateway is returned on upstream errors.' - 'TestRunLive_MissingAPIKey: Verifies key requirement check.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RunRequest' responses: '200': description: 'Completed response text (non-streaming) or stream chunk headers (streaming). Streaming SSE events where each line is formatted as data: {"delta": string, "done": boolean}.' content: application/json: schema: $ref: '#/components/schemas/RunResponse' text/event-stream: schema: type: string '400': description: Missing keys or invalid parameters content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt or version not found content: application/json: schema: $ref: '#/components/schemas/APIError' '502': description: Upstream model provider error content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/projects/{projectID}/prompts/{slug}/batch-run: post: summary: Batch Run Live Prompt Version description: Renders the active 'live' template version and executes it concurrently against multiple variable payloads. operationId: batchRunLive tags: - Prompt Runs security: - BearerAuth: [] parameters: - name: projectID in: path required: true description: The ID of the project the prompt belongs to. schema: type: string format: uuid - name: slug in: path required: true description: Prompt slug, unique within the project. schema: type: string x-edge-cases: - Executes multiple variable payloads concurrently. - Correlates completions or errors for each payload in the batch. x-test-coverage: - 'TestBatchRun_Success_OpenAI: Verifies batch block completions for OpenAI.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/BatchRunRequest' responses: '200': description: Correlated list of model completions content: application/json: schema: $ref: '#/components/schemas/BatchRunResponse' '400': description: Missing keys or invalid parameters content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt or version not found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}/versions: post: summary: Create a Prompt Version description: Creates a new version of the prompt template in 'draft' state. operationId: createVersion tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid x-edge-cases: - Validates template string syntax using Go's text/template parser before creating the entry. - If model is supplied, it must be a non-empty string after trimming whitespace. - If model_params is supplied, it must be a JSON object; nested provider-specific values are supported. x-test-coverage: - 'TestCreateVersion_Success: Verifies version number is incremented to draft status 1.' - 'TestCreateVersion_WithModelConfig: Verifies model and nested model parameters are persisted and returned.' - 'TestCreateVersion_BlankModel: Verifies blank model values reject with 400.' - 'TestCreateVersion_InvalidModelParams: Verifies non-object model parameters reject with 400.' - 'TestCreateVersion_InvalidTemplate: Verifies syntax check fails with 400 and ''invalid template''.' - 'TestCreateVersion_MissingTemplate: Verifies missing template rejects with 400 and ''template is required''.' - 'TestCreateVersion_PromptNotFound: Verifies 404 response on missing parent prompt UUID.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateVersionRequest' responses: '201': description: Version created successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/PromptVersion' required: - version '400': description: Invalid template or syntax error content: application/json: schema: $ref: '#/components/schemas/APIError' examples: missing_template: value: error: template is required syntax_error: value: error: 'invalid template: template parse error...' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '404': description: Prompt not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: prompt not found '500': description: Internal error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: internal error get: summary: List Prompt Versions description: Lists all template versions associated with a prompt container. operationId: listVersions tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: tags in: query required: false description: Optional comma-separated list of tags to filter prompt versions. schema: type: string - name: status in: query required: false description: Optional status to filter prompt versions by (draft, live, archived). schema: type: string enum: - draft - live - archived x-test-coverage: - 'TestListVersions: Verifies active versions listing.' responses: '200': description: List of versions content: application/json: schema: type: object properties: versions: type: array items: $ref: '#/components/schemas/PromptVersion' required: - versions '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '404': description: Prompt not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: prompt not found /v1/prompts/{id}/versions/{version}: get: summary: Get Prompt Version description: Retrieves details of a specific prompt template version by version number. operationId: getVersion tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: version in: path required: true description: Version sequence number (integer) or version tag (string). schema: type: string x-test-coverage: - 'TestGetVersion: Verifies details retrieval of specific version.' - 'TestGetVersion_NotFound: Verifies 404 response on missing version sequence number.' responses: '200': description: Prompt version details content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/PromptVersion' required: - version '400': description: Invalid path formatting content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid version number '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '404': description: Version not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: version not found put: summary: Update Prompt Version Draft description: | Updates the draft template code of a specific version. Only versions currently in the 'draft' status may be modified. operationId: updateVersion tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: version in: path required: true description: Version sequence number (integer) or version tag (string) to update. schema: type: string x-edge-cases: - Fails with 422 Unprocessable Entity if the version status is not 'draft' (e.g. attempting to update a live/archived template). - Allows updating model configuration without changing the template while the version is still a draft. - Allows clearing model and model parameters explicitly by passing JSON null values. x-test-coverage: - 'TestUpdateVersion_Draft: Verifies modification updates template content and returns 200.' - 'TestUpdateVersion_ModelConfigOnly: Verifies model configuration updates on draft versions without changing the template.' - 'TestUpdateVersion_ClearModelConfig: Verifies model and model parameters can be cleared by explicitly sending null.' - 'TestUpdateVersion_LiveVersionRejected: Verifies that updating a published live template version fails with 422 and ''only draft versions can be modified''.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateVersionRequest' responses: '200': description: Version updated successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/PromptVersion' required: - version '400': description: Invalid syntax or missing template content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: template is required '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '404': description: Version not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: version not found '422': description: Only draft versions can be modified content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: only draft versions can be modified delete: summary: Delete Prompt Version Draft description: | Deletes a specific prompt template version. Only versions currently in the 'draft' status may be deleted. operationId: deletePromptVersion tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: version in: path required: true description: Version sequence number (integer) or version tag (string) to delete. schema: type: string x-edge-cases: - Fails with 422 Unprocessable Entity if the version status is not 'draft' (e.g. attempting to delete a live/archived template). x-test-coverage: - 'TestDeleteVersion_Draft: Verifies deletion of specific draft version returns 204.' - 'TestDeleteVersion_LiveVersionRejected: Verifies that deleting a published live template version fails with 422 and ''only draft versions can be deleted''.' responses: '204': description: Version deleted successfully '400': description: Invalid path formatting content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid version number '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '404': description: Version not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: version not found '422': description: Only draft versions can be deleted content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: only draft versions can be deleted /v1/prompts/{id}/versions/{version}/variables: get: summary: Get Template Variables description: Parses the version's Go text/template syntax and returns a list of extracted variables. operationId: getVersionVariables tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid description: The ID of the prompt. - name: version in: path required: true schema: type: string description: The version number or status tag ('live', 'draft', 'stable'). responses: '200': description: Variables extracted successfully content: application/json: schema: type: object properties: variables: type: array items: type: string '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt or version not found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}/versions/{version}/promote: post: summary: Promote Prompt Version description: | Promotes a version of the prompt template along the lifecycle: draft -> stable -> live. Promoting from draft makes it stable (read-only). Promoting from stable makes it live. When promoting a version to live, any previous live version is demoted to stable. operationId: promoteVersion tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: version in: path required: true description: Version sequence number (integer) or version tag (string) to promote. schema: type: string x-edge-cases: - Promotes draft to stable, or stable to live. - Switches previous live versions to stable status automatically. - Returns 422 if the specified version is already live or archived. x-test-coverage: - 'TestPromoteVersion: Verifies promotion path draft -> stable -> live.' - 'TestPromoteVersion_DemotesPreviousLive: Verifies demoting previous live version to stable.' responses: '200': description: Version promoted successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/PromptVersion' required: - version '400': description: Bad parameters content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid version number '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '404': description: Version not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: version not found '422': description: Promotion error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: cannot promote version /v1/prompts/{id}/versions/{version}/demote: post: summary: Demote Prompt Version description: Demotes a live prompt version to stable (making it inactive but remaining read-only). operationId: demoteVersion tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: version in: path required: true description: Version sequence number (integer) or version tag (string) to demote. schema: type: string x-edge-cases: - Returns 422 if version is not live. x-test-coverage: - 'TestDemoteVersion_Success: Verifies demoting live version to stable.' responses: '200': description: Version demoted successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/PromptVersion' required: - version '400': description: Bad parameters content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid version number '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '404': description: Version not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: version not found '422': description: Demotion error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: only live versions can be demoted /v1/prompts/{id}/versions/{version}/archive: post: summary: Archive Prompt Version description: Archives a prompt version, marking its status as archived. operationId: archiveVersion tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: version in: path required: true description: Version sequence number (integer) or version tag (string) to archive. schema: type: string x-edge-cases: - Returns 422 if version is already archived. x-test-coverage: - 'TestArchiveVersion_Success: Verifies archiving a version.' responses: '200': description: Version archived successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/PromptVersion' required: - version '400': description: Bad parameters content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid version number '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '404': description: Version not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: version not found '422': description: Archiving error content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: version is already archived /v1/prompts/{id}/versions/{version}/duplicate: post: summary: Duplicate Prompt Version description: | Copies the specified prompt version's template to create a new prompt version in draft state. This operation does not copy any associated payloads, only the prompt template and model configuration. operationId: duplicateVersion tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: version in: path required: true description: Version sequence number (integer) or version tag (string) to duplicate from. schema: type: string x-edge-cases: - Returns 404 if source version or prompt is not found. - Returns 401 if user is unauthorized. x-test-coverage: - 'TestDuplicateVersion_Success: Verifies duplicating a version creates a new draft version.' - 'TestDuplicateVersion_Errors: Verifies error responses for invalid prompt, missing version, etc.' responses: '201': description: Prompt version duplicated and new draft version created successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/PromptVersion' required: - version '400': description: Bad parameters content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid version number '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '404': description: Version or prompt not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: version not found /v1/projects/{projectID}/prompts/{slug}/versions/{version}/render: post: summary: Render Specific Prompt Version description: Renders a specific template version of a prompt (even draft status) using supplied variables. The prompt is resolved by slug within the given project. operationId: renderVersion tags: - Prompt Renders security: - BearerAuth: [] parameters: - name: projectID in: path required: true description: The ID of the project the prompt belongs to. schema: type: string format: uuid - name: slug in: path required: true description: Prompt slug, unique within the project. schema: type: string - name: version in: path required: true description: Version sequence number (integer) or version tag (string) to render. schema: type: string x-edge-cases: - Fails with 422 when the template references a variable that is not present in the request variables. x-test-coverage: - 'TestRenderVersion_Draft: Verifies that rendering works on draft templates.' - 'TestRenderVersion_MissingVariable: Verifies missing template variables fail with 422 for explicit version renders.' - 'TestRenderVersion_NotFound: Verifies 404 response if the requested version sequence number does not exist.' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RenderRequest' responses: '200': description: Rendered template string content: application/json: schema: $ref: '#/components/schemas/RenderResponse' '400': description: Invalid path formatting content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: invalid version number '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: unauthorized '404': description: Prompt or version not found content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: version not found '422': description: Execution failure content: application/json: schema: $ref: '#/components/schemas/APIError' example: error: 'template execution failed: ...' /v1/projects/{projectID}/prompts/{slug}/versions/{version}/run: post: summary: Run Specific Prompt Version description: Renders a specific template version of a prompt and executes it against the configured model provider. operationId: runVersion tags: - Prompt Runs security: - BearerAuth: [] parameters: - name: projectID in: path required: true description: The ID of the project the prompt belongs to. schema: type: string format: uuid - name: slug in: path required: true description: Prompt slug, unique within the project. schema: type: string - name: version in: path required: true description: Version sequence number (integer) or version tag (string) to execute. schema: type: string requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/RunRequest' responses: '200': description: 'Completed response text or stream chunks (Streaming SSE events where each line is formatted as data: {"delta": string, "done": boolean}).' content: application/json: schema: $ref: '#/components/schemas/RunResponse' text/event-stream: schema: type: string '400': description: Invalid request content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Not found content: application/json: schema: $ref: '#/components/schemas/APIError' '502': description: Provider failure content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}/versions/{version}/tags: post: summary: Attach/Set Version Tag description: Attaches a unique string tag (e.g. 'prod') to the specified prompt version, replacing the tag on any other version of this prompt if it was already assigned. operationId: setVersionTag tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: version in: path required: true description: Version sequence number (integer) or version tag (string) to tag. schema: type: string requestBody: required: true content: application/json: schema: type: object properties: tag: type: string description: Tag string (alphanumeric, dots, dashes, underscores). example: prod maxLength: 50 required: - tag responses: '200': description: Tag attached successfully. Returns the updated version. content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/PromptVersion' required: - version '400': description: Invalid request or tag format content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt or version not found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}/tags: get: summary: List Prompt Version Tags description: Lists all tags associated with versions of this prompt. operationId: listVersionTags tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid responses: '200': description: List of prompt tags content: application/json: schema: type: object properties: tags: type: array items: type: object properties: tag: type: string version: type: integer required: - tag - version required: - tags '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt not found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}/tags/{tag}: delete: summary: Remove Version Tag description: Removes/deletes the specified version tag from the prompt. operationId: removeVersionTag tags: - Prompt Versions security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: tag in: path required: true description: Tag string to remove. schema: type: string responses: '204': description: Tag removed successfully '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt or tag not found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}/payloads: post: summary: Create a Prompt Payload description: Creates a new sample payload for the specified prompt. Only editors of the team can create. operationId: createPromptPayload tags: - Prompt Payloads security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreatePromptPayloadRequest' responses: '201': description: Prompt payload created successfully content: application/json: schema: type: object properties: payload: $ref: '#/components/schemas/PromptPayload' required: - payload '400': description: Invalid request content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt not found content: application/json: schema: $ref: '#/components/schemas/APIError' get: summary: List Prompt Payloads description: Lists all sample payloads associated with the specified prompt. operationId: listPromptPayloads tags: - Prompt Payloads security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid responses: '200': description: List of prompt payloads content: application/json: schema: type: object properties: payloads: type: array items: $ref: '#/components/schemas/PromptPayload' required: - payloads '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt not found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/prompts/{id}/payloads/{payloadID}: get: summary: Get a Prompt Payload description: Retrieves a specific sample payload by its ID and prompt ID. operationId: getPromptPayload tags: - Prompt Payloads security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: payloadID in: path required: true description: Unique payload UUID. schema: type: string format: uuid responses: '200': description: Prompt payload details content: application/json: schema: type: object properties: payload: $ref: '#/components/schemas/PromptPayload' required: - payload '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt or payload not found content: application/json: schema: $ref: '#/components/schemas/APIError' put: summary: Update a Prompt Payload description: Updates an existing sample payload's variables and/or optional name. Only editors can update. operationId: updatePromptPayload tags: - Prompt Payloads security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: payloadID in: path required: true description: Unique payload UUID. schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdatePromptPayloadRequest' responses: '200': description: Prompt payload updated successfully content: application/json: schema: type: object properties: payload: $ref: '#/components/schemas/PromptPayload' required: - payload '400': description: Invalid request content: application/json: schema: $ref: '#/components/schemas/APIError' '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt or payload not found content: application/json: schema: $ref: '#/components/schemas/APIError' delete: summary: Delete a Prompt Payload description: Deletes a specific sample payload. Only editors can delete. operationId: deletePromptPayload tags: - Prompt Payloads security: - BearerAuth: [] parameters: - name: id in: path required: true description: Unique prompt UUID. schema: type: string format: uuid - name: payloadID in: path required: true description: Unique payload UUID. schema: type: string format: uuid responses: '204': description: Prompt payload deleted successfully '401': description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/APIError' '403': description: Forbidden content: application/json: schema: $ref: '#/components/schemas/APIError' '404': description: Prompt or payload not found content: application/json: schema: $ref: '#/components/schemas/APIError' /v1/projects/{projectID}/skills: post: summary: Create a Skill description: Creates a new skill inside a project. Accepts JSON or multipart/form-data with a zip file. operationId: createSkill tags: - Skills security: - BearerAuth: [] parameters: - name: projectID in: path required: true schema: type: string format: uuid description: The ID of the project. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateSkillRequest' multipart/form-data: schema: type: object properties: name: type: string slug: type: string description: type: string file: type: string format: binary required: - name responses: '201': description: Skill created successfully content: application/json: schema: type: object properties: skill: $ref: '#/components/schemas/Skill' '400': description: Invalid request '401': description: Unauthorized '403': description: Forbidden '404': description: Project not found '409': description: Duplicate skill name or slug get: summary: List Skills description: Lists the skills inside a project. operationId: listSkills tags: - Skills security: - BearerAuth: [] parameters: - name: projectID in: path required: true schema: type: string format: uuid description: The ID of the project. responses: '200': description: List of skills content: application/json: schema: type: object properties: skills: type: array items: $ref: '#/components/schemas/Skill' /v1/skills/{id}: get: summary: Get a Skill description: Retrieves details of a specific skill by ID or slug. operationId: getSkill tags: - Skills security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID (UUID) or slug of the skill. responses: '200': description: Skill details '404': description: Skill not found put: summary: Update a Skill description: Updates the metadata of a specific skill. operationId: updateSkill tags: - Skills security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid description: The ID of the skill. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateSkillRequest' responses: '200': description: Skill updated successfully '404': description: Skill not found delete: summary: Delete a Skill description: Deletes a specific skill and all its versions/files. operationId: deleteSkill tags: - Skills security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string format: uuid description: The ID of the skill. responses: '200': description: Skill deleted successfully '404': description: Skill not found /v1/skills/{id}/versions/diff: get: summary: Diff Skill Versions description: Compares the files of two skill versions and returns a unified diff. operationId: diffSkillVersions tags: - Skill Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string - name: from in: query required: true schema: type: string - name: to in: query required: true schema: type: string responses: '200': description: Versions compared successfully content: application/json: schema: type: object properties: from_version: type: integer to_version: type: integer diff: type: string '400': description: Missing or invalid parameters '401': description: Unauthorized '404': description: Skill or version not found /v1/skills/{id}/versions: get: summary: List Skill Versions description: Lists all versions of a skill. operationId: listSkillVersions tags: - Skill Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. responses: '200': description: List of versions post: summary: Create a Skill Version description: Creates a new empty draft version. operationId: createSkillVersion tags: - Skill Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. responses: '201': description: Version created successfully put: summary: Upload a ZIP Archive as a New Version description: Creates a new draft version by uploading a ZIP file containing the version's files. operationId: uploadSkillZip tags: - Skill Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. requestBody: required: true content: multipart/form-data: schema: type: object properties: file: type: string format: binary required: - file responses: '201': description: Version created successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/SkillVersion' /v1/skills/{id}/versions/{version}: get: summary: Get a Skill Version description: Retrieves details of a specific skill version. operationId: getSkillVersion tags: - Skill Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. - name: version in: path required: true schema: type: integer description: The version number. responses: '200': description: Version details '404': description: Version not found delete: summary: Delete a Skill Version description: Deletes a draft version. operationId: deleteSkillVersion tags: - Skill Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. - name: version in: path required: true schema: type: integer description: The version number. responses: '200': description: Version deleted successfully '409': description: Only draft versions can be deleted /v1/skills/{id}/versions/{version}/promote: post: summary: Promote a Skill Version description: Promotes a version status (draft -> stable -> live). operationId: promoteSkillVersion tags: - Skill Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. - name: version in: path required: true schema: type: integer description: The version number. responses: '200': description: Version promoted successfully /v1/skills/{id}/versions/{version}/demote: post: summary: Demote a Skill Version description: Demotes a live version back to stable. operationId: demoteSkillVersion tags: - Skill Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. - name: version in: path required: true schema: type: integer description: The version number. responses: '200': description: Version demoted successfully /v1/skills/{id}/versions/{version}/archive: post: summary: Archive a Skill Version description: Archives a version. operationId: archiveSkillVersion tags: - Skill Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. - name: version in: path required: true schema: type: integer description: The version number. responses: '200': description: Version archived successfully /v1/skills/{id}/versions/{version}/duplicate: post: summary: Duplicate a Skill Version description: Creates a new draft version by duplicating this version's files. operationId: duplicateSkillVersion tags: - Skill Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. - name: version in: path required: true schema: type: integer description: The version number. responses: '201': description: Version duplicated successfully /v1/skills/{id}/versions/{version}/download: get: summary: Download ZIP Archive description: Packs, renders with Go text/template engine, and downloads all files of this version as a ZIP file. Any query parameters, request body variables, or a 'variables' JSON string parameter will be parsed as template context. operationId: downloadSkillZip tags: - Skill Files security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. - name: version in: path required: true schema: type: integer description: The version number. responses: '200': description: The packed ZIP archive content: application/zip: schema: type: string format: binary /v1/skills/{id}/versions/{version}/files: get: summary: List Files in Version description: Lists metadata of all files in this version. operationId: listSkillFiles tags: - Skill Files security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. - name: version in: path required: true schema: type: integer description: The version number. responses: '200': description: List of files metadata post: summary: Upsert an Individual File description: Creates or updates an individual file inside a draft version. operationId: upsertSkillFile tags: - Skill Files security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. - name: version in: path required: true schema: type: integer description: The version number. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpsertFileRequest' responses: '200': description: File saved successfully put: summary: Update an Individual File description: Updates an individual file inside a draft version. operationId: updateSkillFile tags: - Skill Files security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. - name: version in: path required: true schema: type: integer description: The version number. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpsertFileRequest' responses: '200': description: File saved successfully delete: summary: Delete an Individual File description: Deletes an individual file from a draft version. operationId: deleteSkillFile tags: - Skill Files security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. - name: version in: path required: true schema: type: integer description: The version number. - name: file_path in: query required: true schema: type: string description: The path of the file to delete. responses: '200': description: File deleted successfully /v1/skills/{id}/versions/{version}/files/content: get: summary: Get File Content description: Retrieves the path and raw text content of an individual file, rendered with Go text/template using variables passed in query parameters or request body. operationId: getSkillFileContent tags: - Skill Files security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the skill. - name: version in: path required: true schema: type: integer description: The version number. - name: file_path in: query required: true schema: type: string description: The path of the file. responses: '200': description: File content details /v1/projects/{projectID}/tools: post: summary: Create a Tool description: Creates a new tool inside a project. operationId: createTool tags: - Tools security: - BearerAuth: [] parameters: - name: projectID in: path required: true schema: type: string format: uuid description: The ID of the project. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateToolRequest' responses: '201': description: Tool created successfully content: application/json: schema: type: object properties: tool: $ref: '#/components/schemas/Tool' '400': description: Invalid request '401': description: Unauthorized '403': description: Forbidden '404': description: Project not found '409': description: Duplicate tool name or slug get: summary: List Tools description: Lists the tools inside a project. operationId: listTools tags: - Tools security: - BearerAuth: [] parameters: - name: projectID in: path required: true schema: type: string format: uuid description: The ID of the project. responses: '200': description: List of tools content: application/json: schema: type: object properties: tools: type: array items: $ref: '#/components/schemas/Tool' /v1/tools: get: summary: List All Tools (global, with filtering) description: Lists tools globally, optionally filtered by an explicit project ID query parameter. operationId: listAllTools tags: - Tools security: - BearerAuth: [] parameters: - name: project in: query required: false schema: type: string format: uuid description: The ID of the project to filter by. - name: project_id in: query required: false schema: type: string format: uuid description: Alternate query parameter for filtering by project ID. responses: '200': description: List of tools content: application/json: schema: type: object properties: tools: type: array items: $ref: '#/components/schemas/Tool' /v1/tools/{id}: get: summary: Get a Tool description: Retrieves details of a specific tool by ID or slug. operationId: getTool tags: - Tools security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID (UUID) or slug of the tool. responses: '200': description: Tool details content: application/json: schema: type: object properties: tool: $ref: '#/components/schemas/Tool' '404': description: Tool not found put: summary: Update a Tool description: Updates the metadata of a specific tool. operationId: updateTool tags: - Tools security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID (UUID) or slug of the tool. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateToolRequest' responses: '200': description: Tool updated successfully content: application/json: schema: type: object properties: tool: $ref: '#/components/schemas/Tool' '404': description: Tool not found delete: summary: Delete a Tool description: Deletes a specific tool and all its versions. operationId: deleteTool tags: - Tools security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID (UUID) or slug of the tool. responses: '204': description: Tool deleted successfully '404': description: Tool not found /v1/tools/{id}/versions/diff: get: summary: Diff Tool Versions description: Compares the schemas of two tool versions and returns a unified diff. operationId: diffToolVersions tags: - Tool Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string - name: from in: query required: true schema: type: string - name: to in: query required: true schema: type: string responses: '200': description: Versions compared successfully content: application/json: schema: type: object properties: from_version: type: integer to_version: type: integer diff: type: string '400': description: Missing or invalid parameters '401': description: Unauthorized '404': description: Tool or version not found /v1/tools/{id}/versions: get: summary: List Tool Versions description: Lists all versions of a tool. operationId: listToolVersions tags: - Tool Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the tool. responses: '200': description: List of versions content: application/json: schema: type: object properties: versions: type: array items: $ref: '#/components/schemas/ToolVersion' post: summary: Create a Tool Version description: Creates a new empty draft version. operationId: createToolVersion tags: - Tool Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the tool. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateToolVersionRequest' responses: '201': description: Version created successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/ToolVersion' /v1/tools/{id}/versions/{version}: get: summary: Get a Tool Version description: Retrieves details of a specific tool version. operationId: getToolVersion tags: - Tool Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the tool. - name: version in: path required: true schema: type: integer description: The version number. responses: '200': description: Version details content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/ToolVersion' '404': description: Version not found put: summary: Update a Tool Version description: Updates the schemas of a draft tool version. operationId: updateToolVersion tags: - Tool Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the tool. - name: version in: path required: true schema: type: integer description: The version number. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateToolVersionRequest' responses: '200': description: Version updated successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/ToolVersion' '404': description: Version not found '422': description: Only draft versions can be modified delete: summary: Delete or Archive a Tool Version description: Deletes a specific tool version if it is in draft status, otherwise archives it. operationId: deleteToolVersion tags: - Tool Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the tool. - name: version in: path required: true schema: type: integer description: The version number. responses: '204': description: Version deleted successfully '404': description: Version not found /v1/tools/{id}/versions/{version}/promote: post: summary: Promote a Tool Version description: 'Promotes version status: draft -> stable -> live.' operationId: promoteToolVersion tags: - Tool Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the tool. - name: version in: path required: true schema: type: integer description: The version number. responses: '200': description: Version promoted successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/ToolVersion' '404': description: Version not found /v1/tools/{id}/versions/{version}/demote: post: summary: Demote a Tool Version description: 'Demotes version status: live -> stable.' operationId: demoteToolVersion tags: - Tool Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the tool. - name: version in: path required: true schema: type: integer description: The version number. responses: '200': description: Version demoted successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/ToolVersion' '404': description: Version not found /v1/tools/{id}/versions/{version}/archive: post: summary: Archive a Tool Version description: Archives a specific tool version. operationId: archiveToolVersion tags: - Tool Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the tool. - name: version in: path required: true schema: type: integer description: The version number. responses: '200': description: Version archived successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/ToolVersion' '404': description: Version not found /v1/tools/{id}/versions/{version}/duplicate: post: summary: Duplicate a Tool Version description: Creates a new draft version by duplicating the schemas of an existing tool version. operationId: duplicateToolVersion tags: - Tool Versions security: - BearerAuth: [] parameters: - name: id in: path required: true schema: type: string description: The ID or slug of the tool. - name: version in: path required: true schema: type: integer description: The version number to duplicate. responses: '201': description: Version duplicated successfully content: application/json: schema: type: object properties: version: $ref: '#/components/schemas/ToolVersion' '404': description: Version not found components: securitySchemes: BearerAuth: type: http scheme: bearer description: Use an access token retrieved from login (Bearer sess_...) or a programmatic API key (Bearer ak_...). schemas: APIError: type: object properties: error: type: string example: invalid credentials required: - error User: type: object properties: id: type: string format: uuid description: Unique user identifier. email: type: string format: email description: Email address of the user. is_verified: type: boolean description: Whether the user's email has been verified. is_admin: type: boolean description: Whether the user is an admin. created_at: type: string format: date-time description: Timestamp when the user was created. required: - id - email - is_verified - is_admin - created_at Session: type: object properties: id: type: string format: uuid user_id: type: string format: uuid token: type: string description: Session token string (partially masked in lists). expires_at: type: string format: date-time created_at: type: string format: date-time required: - id - user_id - token - expires_at - created_at APIKey: type: object properties: id: type: string format: uuid description: Unique API Key identifier. name: type: string description: Human-readable name given to the API key. org_id: type: string format: uuid description: UUID of the organization. team_id: type: string format: uuid nullable: true description: Optional UUID of the team. operation: type: string enum: - read_render - all - admin description: The scope of operations. created_at: type: string format: date-time description: Timestamp when the API Key was created. last_used_at: type: string format: date-time nullable: true description: Timestamp when the API Key was last used. Null if never used. required: - id - name - org_id - operation - created_at Organization: type: object required: - id - name - created_at properties: id: type: string format: uuid name: type: string created_at: type: string format: date-time OrganizationWithRole: type: object required: - id - name - role - created_at properties: id: type: string format: uuid name: type: string role: type: string enum: - ADMIN - MEMBER example: ADMIN created_at: type: string format: date-time Team: type: object required: - id - name - created_at properties: id: type: string format: uuid org_id: type: string format: uuid name: type: string created_at: type: string format: date-time TeamJoinRequest: type: object required: - id - team_id - user_id - status - created_at - updated_at properties: id: type: string format: uuid team_id: type: string format: uuid user_id: type: string format: uuid status: type: string enum: - pending - approved - rejected created_at: type: string format: date-time updated_at: type: string format: date-time InboxItem: type: object required: - id - type - status - created_at - updated_at - payload properties: id: type: string format: uuid type: type: string enum: - join_request example: join_request status: type: string enum: - pending - approved - rejected created_at: type: string format: date-time updated_at: type: string format: date-time payload: oneOf: - $ref: '#/components/schemas/JoinRequestPayload' JoinRequestPayload: type: object required: - user_id - user_email - team_id - team properties: user_id: type: string format: uuid user_email: type: string format: email team_id: type: string format: uuid team: $ref: '#/components/schemas/Team' Prompt: type: object properties: id: type: string format: uuid description: Unique identifier. project_id: type: string format: uuid description: Unique identifier of the project the prompt belongs to. slug: type: string description: Unique slug within the project. name: type: string description: Name of the prompt container. description: type: string description: Brief summary explaining the prompt purpose. status: type: string enum: - active - archived description: Current status of the prompt container (active, archived). created_at: type: string format: date-time updated_at: type: string format: date-time required: - id - project_id - slug - name - description - status - created_at - updated_at PromptVersion: type: object properties: id: type: string format: uuid description: Unique version identifier. prompt_id: type: string format: uuid description: Reference to parent Prompt ID. version: type: integer description: Incrementing sequence number of the version. template: type: string description: The template syntax code with Go template parameters. example: Hello {{.name}}! status: type: string enum: - draft - stable - live - archived description: Active lifecycle status of this template version. model: type: string nullable: true description: Optional `provider/model` identifier intended to execute this prompt version. example: anthropic/claude-opus-4-6 model_params: type: object nullable: true additionalProperties: true description: Optional provider-specific model parameters. Nested values are supported. example: temperature: 0.7 max_tokens: 1024 thinking: type: enabled budget_tokens: 512 created_at: type: string format: date-time published_at: type: string format: date-time nullable: true description: Timestamp when status was set to live. Null if draft. tags: type: array items: type: string description: List of tag strings attached to this version. required: - id - prompt_id - version - template - status - model - model_params - created_at - published_at - tags RenderRequest: type: object properties: variables: type: object additionalProperties: true description: Key-value dictionary of arguments interpolated into the prompt template. example: name: Alice count: 5 RenderResponse: type: object properties: rendered: type: string description: Fully parsed template response with variable replacements. example: Hello, Alice! Count is 5. version: type: integer description: Sequence version number that was executed. example: 1 slug: type: string description: Unique prompt slug. example: my_prompt tags: type: array items: type: string description: List of tag strings assigned to this version. model: type: string nullable: true description: Optional `provider/model` identifier intended to execute this prompt version. example: anthropic/claude-opus-4-6 model_params: type: object nullable: true additionalProperties: true description: Optional provider-specific model parameters. Nested values are supported. example: temperature: 0.7 max_tokens: 1024 required: - rendered - version - slug - tags - model - model_params RunRequest: type: object properties: variables: type: object additionalProperties: true description: Variables to interpolate into the template. stream: type: boolean description: Set to true to receive token stream instead of a blocked response. default: false model: type: string nullable: true description: 'Ad-hoc model override. Must start with a supported provider prefix: ''openai/'', ''anthropic/'', ''gemini/'', ''deepseek/'', ''groq/'', or ''openrouter/''. All providers except ''anthropic/'' follow standard OpenAI-compatible JSON request, response, and SSE streaming conventions.' model_params: type: object nullable: true additionalProperties: true description: Ad-hoc model parameters override. RunResponse: type: object properties: response: type: string description: Text content produced by the model provider completion. example: Greetings from the model! model: type: string description: The model identifier that was executed. example: openai/gpt-4o version: type: integer description: The prompt version number that was used. example: 2 slug: type: string description: Prompt slug. example: my-prompt required: - response - model - version - slug BatchRunRequest: type: object properties: batch: type: array items: type: object properties: variables: type: object additionalProperties: true model: type: string nullable: true model_params: type: object nullable: true additionalProperties: true required: - batch BatchRunResponse: type: object properties: results: type: array items: type: object properties: response: type: string error: type: string model: type: string version: type: integer slug: type: string required: - results - model - version - slug PromptPayload: type: object properties: id: type: string format: uuid description: Unique payload identifier. prompt_id: type: string format: uuid description: Reference to parent Prompt ID. name: type: string nullable: true description: Optional name for this sample payload. variables: type: object description: Standard JSON object containing variable names and sample values. example: user: Arpit role: Admin created_at: type: string format: date-time updated_at: type: string format: date-time required: - id - prompt_id - variables - created_at - updated_at CreatePromptPayloadRequest: type: object properties: variables: type: object description: JSON object containing variable names and sample values. example: user: Arpit role: Admin required: - variables UpdatePromptPayloadRequest: type: object properties: name: type: string description: Optional name for this sample payload. example: Admin Sample variables: type: object description: JSON object containing variable names and sample values. example: user: Alice role: Editor UpdatePromptRequest: type: object properties: description: type: string description: Brief summary explaining the prompt purpose. example: Useful greeting prompt Skill: type: object properties: id: type: string format: uuid project_id: type: string format: uuid name: type: string slug: type: string description: type: string created_at: type: string format: date-time updated_at: type: string format: date-time required: - id - project_id - name - slug SkillVersion: type: object properties: id: type: string format: uuid skill_id: type: string format: uuid version: type: integer status: type: string created_at: type: string format: date-time updated_at: type: string format: date-time required: - id - skill_id - version - status CreateSkillRequest: type: object properties: name: type: string slug: type: string description: type: string required: - name UpdateSkillRequest: type: object properties: name: type: string slug: type: string description: type: string UpsertFileRequest: type: object properties: file_path: type: string content: type: string required: - file_path - content Tool: type: object properties: id: type: string format: uuid project_id: type: string format: uuid slug: type: string name: type: string description: type: string url: type: string format: uri created_at: type: string format: date-time updated_at: type: string format: date-time required: - id - project_id - slug - name ToolVersion: type: object properties: id: type: string format: uuid tool_id: type: string format: uuid version: type: integer input_schema: type: object description: JSON Schema for tool inputs. output_schema: type: object description: JSON Schema for tool outputs. status: type: string enum: - draft - stable - live - archived created_at: type: string format: date-time updated_at: type: string format: date-time required: - id - tool_id - version - input_schema - output_schema - status CreateToolRequest: type: object properties: name: type: string slug: type: string description: type: string url: type: string format: uri required: - name UpdateToolRequest: type: object properties: name: type: string slug: type: string format: uri description: type: string url: type: string format: uri required: - name CreateToolVersionRequest: type: object properties: input_schema: type: object description: Optional JSON Schema for tool inputs. output_schema: type: object description: Optional JSON Schema for tool outputs. UpdateToolVersionRequest: type: object properties: input_schema: type: object description: Optional JSON Schema for tool inputs. output_schema: type: object description: Optional JSON Schema for tool outputs. SearchEntityType: type: string enum: - prompt - skill - tool example: prompt SearchResult: type: object description: Common searchable metadata. Use type and id with the corresponding registry endpoint to retrieve the full entity. properties: type: $ref: '#/components/schemas/SearchEntityType' id: type: string format: uuid project_id: type: string format: uuid slug: type: string name: type: string description: type: string created_at: type: string format: date-time updated_at: type: string format: date-time required: - type - id - project_id - slug - name - description - created_at - updated_at SearchResponse: type: object properties: results: type: array items: $ref: '#/components/schemas/SearchResult' required: - results RegisterRequest: type: object properties: email: type: string format: email example: user@example.com password: type: string minLength: 8 example: secretpassword123 team_id: type: string format: uuid example: f47ac10b-58cc-4372-a567-0e02b2c3d479 required: - email - password LoginRequest: type: object properties: email: type: string format: email example: user@example.com password: type: string example: secretpassword123 required: - email - password VerifyRequest: type: object properties: email: type: string format: email example: user@example.com code: type: string example: '123456' required: - email - code UpdateMeRequest: type: object properties: email: type: string format: email example: newemail@example.com required: - email ChangePasswordRequest: type: object properties: current_password: type: string example: OldSecretPassword123! new_password: type: string minLength: 8 example: NewSecretPassword123! required: - current_password - new_password TriggerPasswordResetRequest: type: object properties: email: type: string format: email example: user@example.com required: - email ResetPasswordRequest: type: object properties: code: type: string example: '123456' new_password: type: string minLength: 8 example: NewSecretPassword123! required: - code - new_password CreateAPIKeyRequest: type: object properties: name: type: string description: Descriptive name for the key. example: ci-pipeline org_id: type: string format: uuid description: Organization that owns the API key. team_ids: type: array description: Optional list of teams the key can access. Omit or pass an empty array for organization-wide access. items: type: string format: uuid operation: type: string enum: - read_render - all - admin default: read_render required: - name - org_id APIKeyCreatedResponse: type: object properties: id: type: string format: uuid description: Unique API Key identifier. name: type: string description: Human-readable name of the key. key: type: string description: The fully generated secure raw API Key string. This is returned ONLY ONCE. example: ak_7f9ba3271cf881309d9be8c9c0fcae47a95b8d29c3f0b2da8e89cf21e5c3df01 operation: type: string created_at: type: string format: date-time description: Timestamp of creation. required: - id - name - key - operation - created_at UpdateAPIKeyRequest: type: object properties: name: type: string description: Descriptive name for the key. team_ids: type: array description: Optional list of teams the key can access. Omit or pass an empty array for organization-wide access. items: type: string format: uuid operation: type: string enum: - read_render - all - admin TeamMemberResponse: type: object required: - user_id - email - role - created_at properties: user_id: type: string format: uuid email: type: string role: type: string created_at: type: string format: date-time UpdateOrgMemberRoleRequest: type: object required: - role properties: role: type: string enum: - admin - member description: The new role for the member. 'admin' promotes to organization admin, 'member' demotes to standard member. AuditLog: type: object required: - id - org_id - action - entity_type - metadata - created_at properties: id: type: string format: uuid org_id: type: string format: uuid actor_id: type: string format: uuid nullable: true action: type: string entity_type: type: string entity_id: type: string format: uuid nullable: true metadata: type: object additionalProperties: true created_at: type: string format: date-time CreateProjectRequest: type: object required: - team_id - name properties: team_id: type: string format: uuid description: The team that will own the project. name: type: string description: Human-readable project name. slug: type: string description: Optional normalized slug; derived from the name when omitted. Project: type: object required: - id - owning_team_id - name - slug - created_at properties: id: type: string format: uuid owning_team_id: type: string format: uuid description: The team that owns the project. name: type: string description: Human-readable project name, unique within the owning team. slug: type: string description: Normalized identifier, unique within the owning team. created_at: type: string format: date-time ProjectResponse: type: object required: - project properties: project: $ref: '#/components/schemas/Project' UpdateProjectRequest: type: object properties: name: type: string description: Human-readable project name. slug: type: string description: Normalized identifier, unique within the owning team. GrantProjectAccessRequest: type: object required: - team_id properties: team_id: type: string format: uuid description: The team to grant access to. Must be in the owning team's organization. ProjectAccess: type: object required: - project_id - team_id properties: project_id: type: string format: uuid team_id: type: string format: uuid ProjectAccessResponse: type: object required: - access properties: access: $ref: '#/components/schemas/ProjectAccess' ProjectListResponse: type: object required: - projects properties: projects: type: array items: $ref: '#/components/schemas/Project' CreatePromptRequest: type: object properties: name: type: string example: My Prompt description: type: string example: Useful prompt slug: type: string example: my_prompt required: - name CreateVersionRequest: type: object properties: template: type: string example: Hello, {{.name}}! model: type: string description: Optional `provider/model` identifier intended to execute this prompt version. example: anthropic/claude-opus-4-6 model_params: type: object additionalProperties: true description: Optional provider-specific model parameters. Nested values are supported. example: temperature: 0.7 max_tokens: 1024 required: - template UpdateVersionRequest: type: object properties: template: type: string example: Hello, {{.name}}! Count is {{.count}}. model: type: string nullable: true description: Optional `provider/model` identifier intended to execute this prompt version. example: openai/gpt-4.1-mini model_params: type: object nullable: true additionalProperties: true description: Optional provider-specific model parameters. Nested values are supported. example: temperature: 0.2